packages feed

futhark 0.14.1 → 0.15.1

raw patch · 152 files changed

+13086/−6615 lines, 152 filesdep ~neat-interpolation

Dependency ranges changed: neat-interpolation

Files

+ docs/binary-data-format.rst view
@@ -0,0 +1,79 @@+.. _binary-data-format:++Binary Data Format+==================++Futhark programs compiled to an executable support both textual and binary+input. Both are read via standard input, and can be mixed, such that one+argument to an entry point may be binary, and another may be textual. The binary+input format takes up significantly less space on disk, and can be read much+faster than the textual format. This chapter describes the binary input format+and its current limitations. The input formats (whether textual or binary) are+not used for Futhark programs compiled to libraries, which instead use whichever+format is supported by their host language.++Currently reading binary input is only supported for programs+generated by ``futhark c``/``futhark opencl``, and+``futhark py``/``futhark pyopencl``.  It is *not* supported for+``futhark run``.++You can generate random data in the binary format with ``futhark+dataset`` (:ref:`futhark-dataset(1)`).  This tool can also be used to+convert between binary and textual data.++Futhark-generated executables can be asked to generate binary output+with the ``-b`` option.++Specification+-------------++Elements that are bigger than one byte are always stored using little endian --+we mostly run our code on x86 hardware so this seemed like a reasonable choice.++When reading input for an argument to the entry function, we need to be able to+differentiate between text and binary input. If the first non-whitespace+character of the input is a ``b`` we will parse this argument as binary,+otherwise we will parse it in text format. Allowing preceding whitespace+characters makes it easy to use binary input for some arguments, and text input+for others.++The general format has this header::++  b <version> <num_dims> <type>++Where ``version`` is a byte containing the version of the binary format used for+encoding (currently 2), ``num_dims`` is the number of dimensions in the array as+a single byte (0 for scalar), and ``type`` is a 4 character string describing+the type of the values(s) -- see below for more details.++Encoding a scalar value is done by treating it as a 0-dimensional array::++  b <version> 0 <type> <value>++To encode an array we must encode the number of dimensions ``n`` as a single+byte, each dimension ``dim_i`` as an unsigned 64-bit little endian integer, and+finally all the values in their binary little endian representation::++  b <version> <n> <type> <dim_1> <dim_2> ... <dim_n> <values>+++Type Values+~~~~~~~~~~~++A type is identified by a 4 character ASCII string (four bytes). Valid+types are::++  "  i8"+  " i16"+  " i32"+  " i64"+  "  u8"+  " u16"+  " u32"+  " u64"+  " f32"+  " f64"+  "bool"++Note that unsigned and signed integers have the same byte-level+representation.
+ docs/c-porting-guide.rst view
@@ -0,0 +1,178 @@+.. _c-porting-guide:++C Porting Guide+===============++This short document contains a collection of tips and tricks for+porting simple numerical C code to Futhark.  Futhark's sequential+fragment is powerful enough to permit a rather straightforward+translation of sequential C code that does not rely on pointer+mutation.  Additionally, we provide hints on how to recognise C coding+patterns that are symptoms of C's weak type system, and how better to+organise it in Futhark.++One intended audience of this document is a programmer who needs to+translate a benchmark application written in C, or needs to use a+simple numerical algorithm that is already available in the form of C+source code.++Where This Guide Falls Short+----------------------------++Some C code makes use of unstructured returns and nonlocal exits+(``return`` inside loops, for example).  These are not easy to express+in Futhark, and will require massaging the control flow a bit.  C code+that uses ``goto`` is likewise not easy to port.++Types+-----++Futhark provides scalar types that match the ones commonly used in C:+``u8``/``u16``/``u32``/``u64`` for the unsigned integers,+``i8``/``i16``/``i32``/``i64`` for the signed, and ``f32``/``f64`` for+``float`` and ``double`` respectively.  In contrast to C, Futhark does+not automatically promote types in expressions - you will have to+manually make sure that both operands to e.g. a multiplication are of+the exact same type.  This means that you will need to understand+exactly which types a given expression in original C program operates+on, which generally boils down to converting the type of the+(type-wise) smaller operand to that of the larger.  Note that the+Futhark ``bool`` type is not considered a number.++Operators+---------++Most of the C operators can be found in Futhark with their usual+names.  Note however that the Futhark ``/`` and ``%`` operators for+integers round towards negative infinity, whereas their counterparts+in C round towards zero.  You can write ``//`` and ``%%`` if you want+the C behaviour.  There is no difference if both operands are+non-zero, but ``//`` and ``%%`` may be slightly faster.  For unsigned+numbers, they are exactly the same.++Variable Mutation+-----------------++As a sequential language, most C programs quite obviously rely heavily+on mutating variables.  However, in many programs, this is done in a+static manner without indirection through pointers (except for arrays;+see below), which is conceptually similar to just declaring a new+variable of the same name that shadows the old one.  If this is the+case, a C assignment can generally be translated to just a+``let``-binding.  As an example, let us consider the following+function for computing the modular multiplicative inverse of a 16-bit+unsigned integer (part of the IDEA encryption algorithm):++.. code-block:: c++  static uint16_t ideaInv(uint16_t a) {+    uint32_t b;+    uint32_t q;+    uint32_t r;+    int32_t t;+    int32_t u;+    int32_t v;++    b = 0x10001;+    u = 0;+    v = 1;++    while(a > 0)+      {+        q = b / a;+        r = b % a;++        b = a;+        a = r;++        t = v;+        v = u - q * v;+        u = t;+      }++    if(u < 0)+      u += 0x10001;++    return u;+  }++Each iteration of the loop mutates the variables ``a``, ``b``, ``v``,+and ``u`` in ways that are visible to the following iteration.+Conversely, the "mutations" of ``q``, ``r``, and ``t`` are not truly+mutations, and the variable declarations could be moved inside the+loop if we wished.  Presumably, the C programmer left them outside for+aesthetic reasons.  When translating such code, it is important to+determine exactly how much *true* mutation is going on, and how much+is just reuse of variable space.  This can usually be done by checking+whether a variable is read before it is written in any given+iteration - if not, then it is not true mutation.  The variables that+change value from one iteration of the loop to the next will need to+be maintained as *merge parameters* of the Futhark ``do``-loop.++The Futhark program resulting from a straightforward port looks as+follows:++.. code-block:: futhark++  let main(a: u16): u32 =+    let b = 0x10001u32+    let u = 0i32+    let v = 1i32 in+    let (_,_,u,_) = loop ((a,b,u,v)) while a > 0u16 do+      let q = b / u32.u16(a)+      let r = b % u32.u16(a)++      let b = u32.u16(a)+      let a = u16.u32(r)++      let t = v+      let v = u - i32.u32 (q) * v+      let u = t in+      (a,b,u,v)++    in u32.i32(if u < 0 then u + 0x10001 else u)++Note the heavy use of type conversion and type suffixes for constants.+This is necessary due to Futhark's lack of implicit conversions.  Note+also the conspicuous way in which the ``do``-loop is written - the+result of a loop iteration consists of variables whose names are+identical to those of the merge parameters.++This program can still be massaged to make it more idiomatic Futhark -+for example, the variable ``t`` only serves to store the old value of+``v`` that is otherwise clobbered.  This can be written more elegantly+by simply inlining the expressions in the result part of the loop+body.++Arrays+------++Dynamically sized multidimensional arrays are somewhat awkward in C,+and are often simulated via single-dimensional arrays with explicitly+calculated indices:++.. code:: c++  a[i * M + j] = foo;++This indicates a two-dimensional array ``a`` whose *inner* dimension+is of size ``M``.  We can usually look at where ``a`` is allocated to+figure out what the size of the outer dimension must be:++.. code:: c++  a = malloc(N * M * sizeof(int));++We see clearly that ``a`` is a two-dimensional integer array of size+``N`` times ``M`` - or of type ``[N][M]i32`` in Futhark.  Thus, the update+expression above would be translated as::++  let a[i,j] = foo in+  ...++C programs usually first allocate an array, then enter a loop to+provide its initial values.  This is not possible in Futhark -+consider whether you can write it as a ``replicate``, an ``iota``, or+a ``map``.  In the worst case, use ``replicate`` to obtain an array of+the desired size, then use a ``do``-loop with in-place updates to+initialise it (but note that this will run stricly sequentially).
docs/conf.py view
@@ -60,13 +60,12 @@ # # The short X.Y version. -# No reason for a full YAML parser; let's just hack it.+# No reason for a cabal file parser; let's just hack it. def get_version():-    # Get lines-    lines = open('../package.yaml', 'r').read().split('\n')-    # Find version line.-    version_line = lines[1]-    return re.search('version: "(.*)"', version_line).group(1)+    # Get cabal file+    cabal_file = open('../futhark.cabal', 'r').read()+    # Extract version+    return re.search('version:[ ]*([^ ]*)', cabal_file).group(1)  version = get_version() # The full version, including alpha/beta/rc tags.@@ -109,7 +108,7 @@      tokens = {         'root': [-            (r'if|then|else|let|loop|in|val|for|do|with|local|open|include|import|type|entry|module|while|unsafe|module', token.Keyword),+            (r'(if|then|else|let|loop|in|val|for|do|with|local|open|include|import|type|entry|module|while|unsafe|module)\b', token.Keyword),             (r"[a-zA-Z_][a-zA-Z0-9_']*", token.Name),             (r"-- .*", token.Comment),             (r'.', token.Text)
+ docs/hacking.rst view
@@ -0,0 +1,91 @@+.. _hacking:++Hacking on the Futhark Compiler+===============================++The Futhark compiler is a significant body of code with a not entirely+straightforward design.  The main reference is the `documentation of+the compiler internals`_ that is automatically generated by Haddock.+If you feel that it is incomplete, or lacks an explanation, then feel+free to report it as an issue on the `GitHub page`_.  Documentation+bugs are bugs too.++.. _`documentation of the compiler internals`: https://futhark-lang.org/haddock/+.. _`GitHub page`: https://github.com/diku-dk/futhark++The Futhark compiler is built using `Stack`_.  It's a good idea to+familiarise yourself with how it works.  As a starting point, here are+a few hints:++  * When testing, pass ``--fast`` to ``stack`` to disable the GHC+    optimiser.  This speeds up builds considerably (although it still+    takes a while).  The resulting Futhark compiler will run slower,+    but it is not something you will notice for small test programs.++  * When debugging, pass ``--profile`` to ``stack``.  This will build+    the Futhark compiler with debugging information (not just+    profiling).  In particular, hard crashes will print a stack trace.+    You can also get actual profiling information by passing+    ``+RTS -pprof-all -RTS`` to the Futhark compiler.  This asks the+    Haskell runtime to print profiling information to a file.  For+    more information, see the `Profiling`_ chapter in the GHC User+    Guide.++  * You may wish to set the environment variable+    ``FUTHARK_COMPILER_DEBUGGING=1``.  Currently this only has the+    effect of making the frontend print internal names, but it may+    control more things in the future.++.. _`stack`: https://docs.haskellstack.org/en/stable/README/+.. _`Profiling`: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/profiling.html++Debugging Internal Type Errors+------------------------------++The Futhark compiler uses a typed core language, and the type checker+is run after every pass.  If a given pass produces a program with+inconsistent typing, the compiler will report an error and abort.+While not every compiler bug will manifest itself as a core language+type error (unfortunately), many will.  To write the erroneous core+program to a file in case of type error, pass ``-v filename`` to the+compiler.  This will also enable verbose output, so you can tell which+pass fails.  The ``-v`` option is also useful when the compiler itself+crashes, as you can at least tell where in the pipeline it got to.++Checking Generated Code+-----------------------++Hacking on the compiler will often involve inspecting the quality of+the generated code.  The recommended way to do this is to use+:ref:`futhark-c(1)` or :ref:`futhark-opencl(1)` to compile a Futhark+program to an executable.  These backends insert various forms of+instrumentation that can be enabled by passing run-time options to the+generated executable.++  * As a first resort, use ``-t`` option to use the built-in runtime+    measurements.  A nice trick is to pass ``-t /dev/stderr``, while+    redirecting standard output to ``/dev/null``.  This will print the+    runtime on the screen, but not the execution result.++  * Optionally use ``-r`` to ask for several runs, e.g. ``-r 10``.  If+    combined with ``-t``, this will cause several runtimes to be+    printed (one per line).  The :ref:`futhark-bench(1)` tool itself+    uses ``-t`` and ``-r`` to perform its measurements.++  * Pass ``-D`` to have the program print information on allocation+    and deallocation of memory.++  * (:ref:`futhark-opencl(1)` only) Use the ``-D`` option to enable+    synchronous execution.  ``clFinish()`` will be called after most+    OpenCL operations, and a running log of kernel invocations will be+    printed.  At the end of execution, the program prints a table+    summarising all kernels and their total runtime and average+    runtime.++Using ``futhark dev``+---------------------++For debugging specific compiler passes, the ``futhark dev`` subcommand+allows you to tailor your own compilation pipeline using command line+options.  It is also useful for seeing what the AST looks like after+specific passes.
+ docs/installation.rst view
@@ -0,0 +1,337 @@+.. _installation:++Installation+============++There are two main ways to install the Futhark compiler: using a+precompiled tarball or compiling from source.  Both methods are+discussed below.  If you are using Linux, see+:ref:`linux-installation`.  If you are using Windows, make sure to+read :ref:`windows-installation`.  If you are using macOS, read+:ref:`macos-installation`.++Futhark is also available via `Nix <https://nixos.org/nix/>`_.  If you+are using Nix, simply install the ``futhark`` derivation from Nixpkgs.++Dependencies+------------++On non-Windows, you will need to have the ``gmp`` and ``tinfo``+libraries installed.  These are pretty common, so you may already have+them.  On Debian-like systems (e.g. Ubuntu), use::++  sudo apt install libtinfo-dev libgmp-dev++If you install Futhark via a package manager (e.g. Homebrew, Nix, or+AUR), you shouldn't need to worry about this.++Compiling from source+---------------------++We use the the `Haskell Tool Stack`_ to handle dependencies and+compilation of the Futhark compiler, so you will need to install the+``stack`` tool.  Fortunately, the ``stack`` developers provide ample+documentation about `installing Stack`_ on a multitude of operating+systems.  If you're lucky, it may even be in your local package+repository.++You can either retrieve a `source release tarball+<https://github.com/diku-dk/futhark/releases>`_ or perform a checkout+of our Git repository::++  $ git clone https://github.com/diku-dk/futhark.git++This will create a directory ``futhark``, which you must enter::++  $ cd futhark++To get all the prerequisites for building the Futhark compiler+(including, if necessary, the appropriate version of the Haskell+compiler), run::++  $ stack setup++Note that this will not install anything system-wide and will have no+effect outside the Futhark build directory. Now you can run the+following command to build the Futhark compiler, including all+dependencies::++  $ stack build++The Futhark compiler and its tools will now be built.  This step+typically requires at least 8GiB of memory.  You may be able to build+it on a smaller machine by adding the ``--fast`` option, although the+resulting Futhark compiler binary will run slower.++After building, you can copy the binaries to your ``$HOME/.local/bin``+directory by running::++  $ stack install++Note that this does not install the Futhark manual pages.++Installing from a precompiled snapshot+--------------------------------------++Tarballs of binary releases can be `found online+<https://futhark-lang.org/releases/>`_, but are available only for+very few platforms (as of this writing, only GNU/Linux on x86_64).+See the enclosed ``README.md`` for installation instructions.++Furthermore, every day a program automatically clones the Git+repository, builds the compiler, and packages a simple tarball+containing the resulting binaries, built manpages, and a simple+``Makefile`` for installing.  The implication is that these tarballs+are not vetted in any way, nor more stable than Git HEAD at any+particular moment in time.  They are provided for users who wish to+use the most recent code, but are unable to compile Futhark+themselves.++At the moment, we build such snapshots only for a single operating+system:++Linux (x86_64)+  `futhark-nightly-linux-x86_64.tar.xz <https://futhark-lang.org/releases/futhark-nightly-linux-x86_64.tar.xz>`_++In time, we hope to make snapshots available for more platforms, but+we are limited by system availability.++.. _`Haskell tool stack`: http://docs.haskellstack.org/+.. _`installing Stack`: http://docs.haskellstack.org/#how-to-install++.. _linux-installation:++Installing Futhark on Linux+---------------------------++* `Linuxbrew`_ is a distribution-agnostic package manager that+  contains a formula for Futhark.  If Linuxbrew is installed (which+  does not require ``root`` access), installation is as easy as::++    $ brew install futhark++  Note that as of this writing, Linuxbrew is hampered by limited+  compute resources for building packages, so the Futhark version may+  be a bit behind.++* Arch Linux users can use a `futhark-nightly package+  <https://aur.archlinux.org/packages/futhark-nightly/>`_.++Otherwise (or if the version in the package system is too old), your+best bet is to install from source or use a tarball, as described+above.++.. _`Linuxbrew`: http://linuxbrew.sh/++.. _macos-installation:++Using OpenCL or CUDA+~~~~~~~~~~~~~~~~~~~~++If you wish to use ``futhark opencl`` or ``futhark cuda``, you must+have the OpenCL or CUDA libraries installed, respectively.  Consult+your favourite search engine for instructions on how to do this on+your distribution.  It is usually not terribly difficult if you+already have working GPU drivers.++For OpenCL, note that there is a distinction between the general+OpenCL host library (``OpenCL.so``) that Futhark links against, and+the *Installable Client Driver* (ICD) that OpenCL uses to actually+talk to the hardware.  You will need both.  Working display drivers+for the GPU does not imply that an ICD has been installed - they are+usually in a separate package.  Consult your favourite search engine+for details.++Installing Futhark on macOS+---------------------------++Futhark is available on `Homebrew`_, and the latest release can be+installed via::++  $ brew install futhark++Or you can install the unreleased development version with::++  $ brew install --HEAD futhark++This has to compile from source, so it takes a little while (20-30+minutes is common).++macOS ships with one OpenCL platform and various devices.  One of+these devices is always the CPU, which is not fully functional, and is+never picked by Futhark by default.  You can still select it manually+with the usual mechanisms (see :ref:`executable-options`), but it is+unlikely to be able to run most Futhark programs.  Depending on the+system, there may also be one or more GPU devices, and Futhark will+simply pick the first one as always.  On multi-GPU MacBooks, this is+is the low-power integrated GPU.  It should work just fine, but you+might have better performance if you use the dedicated GPU instead.+On a Mac with an AMD GPU, this is done by passing ``-dAMD`` to the+generated Futhark executable.++.. _`Homebrew`: https://brew.sh/++.. _windows-installation:++Setting up Futhark on Windows+-----------------------------++The Futhark compiler itself is easily installed on Windows via+``stack`` (see above).  If you are using the default Windows console,+you may need to run ``chcp 65001`` to make Unicode characters show up+correctly.++It takes a little more work to make the OpenCL and PyOpenCL backends+functional.  This guide was last updated on the 5th of May 2016, and+is for computers using 64-bit Windows along with `CUDA 7.5`_ and+Python 2.7 (`Anaconda`_ preferred).++Also `Git for Windows`_ is required for its Linux command line tools.+If you have not marked the option to add them to path, there are+instructions below how to do so. The GUI alternative to ``git``,+`Github Desktop`_ is optional and does not come with the required+tools.++.. _`CUDA 7.5`: https://developer.nvidia.com/cuda-downloads+.. _`Anaconda`: https://www.continuum.io/downloads#_windows+.. _`Git for Windows`: https://git-scm.com/download/win+.. _`Github Desktop`: https://desktop.github.com/++Setting up Futhark and OpenCL+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++1) Clone the Futhark repository to your hard drive.++2) Install `Stack`_ using the 64-bit installer.  Compile the Futhark+   compiler as described in :ref:`installation`.++3) For editing environment variables it is strongly recommended that+   you install the `Rapid Environment Editor`_++4) For a Futhark compatible C/C++ compiler, that you will also need to+   install pyOpenCL later, install MingWpy. Do this using the ``pip+   install -i https://pypi.anaconda.org/carlkl/simple mingwpy``+   command.++5) Assuming you have the latest Anaconda distribution as your primary+   one, it will get installed to a place such as+   ``C:\Users\UserName\Anaconda2\share\mingwpy``. The pip installation+   will not add its bin or include directories to path.++   To do so, open the Rapid Environment Editor and add+   ``C:\Users\UserName\Anaconda2\share\mingwpy\bin`` to the system-wide+   ``PATH`` variable.++   If you have other MingW or GCC distributions, make sure MingWpy takes+   priority by moving its entry above the other distributions. You can+   also change which Python distribution is the default one using the+   same trick should you need so.++   If have done so correctly, typing ``where gcc`` in the command prompt+   should list the aforementioned MingWpy installation at the top or show+   only it.++   To finish the installation, add the+   ``C:\Users\UserName\Anaconda2\share\mingwpy\include`` to the ``CPATH``+   environment variable (note: *not* ``PATH``). Create the variable if+   necessary.++6) The header files and the .dll for OpenCL that comes with the CUDA+   7.5 distribution also need to be installed into MingWpy.  Go to+   ``C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\include``+   and copy the ``CL`` directory into the MingWpy ``include`` directory.++   Next, go to ``C:\Program Files\NVIDIA Corporation\OpenCL`` and copy+   the ``OpenCL64.dll`` file into the MingWpy ``lib`` directory (it is+   next to ``include``).++   The CUDA distribution also comes with the static ``OpenCL.lib``, but+   trying to use that one instead of the ``OpenCL64.dll`` will cause+   programs compiled with ``futhark opencl`` to crash, so ignore it+   completely.++Now you should be able to compile with ``futhark opencl`` and run+Futhark programs on the GPU.++Congratulations!++.. _`Stack`: http://docs.haskellstack.org/en/stable/install_and_upgrade/#windows+.. _`Rapid Environment Editor`: http://www.rapidee.com/en/about++Setting up PyOpenCL+~~~~~~~~~~~~~~~~~~~++The following instructions are for how to setup the+``futhark-pyopencl`` backend.++First install Mako using ``pip install mako``.++Also install PyPNG using ``pip install pypng`` (not stricly necessary,+but some examples make use of it).++7) Clone the `PyOpenCL repository`_ to your hard drive. Do+   this instead of downloading the zip, as the zip will not contain+   some of the other repositories it links to and you will end up with+   missing header files.++8) If you have ignored the instructions and gotten Python 3.x instead+   2.7, you will have to do some extra work.++   Edit ``.\pyopencl\compyte\ndarray\gen_elemwise.py`` and+   ``.\pyopencl\compyte\ndarray\test_gpu_ndarray.py`` and convert most+   Python 2.x style print statements to Python 3 syntax. Basically wrap+   print arguments in brackets "(..)" and ignore any lines containing+   StringIO ``>>`` operator.++   Otherwise just go to the next point.++9) Go into the repo directory and from the command line execute+   ``python configure.py``.++   Edit ``siteconf.py`` to following::++     CL_TRACE = false+     CL_ENABLE_GL = false+     CL_INC_DIR = ['c:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v7.5\\include']+     CL_LIB_DIR = ['C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v7.5\\lib\\x64']+     CL_LIBNAME = ['OpenCL']+     CXXFLAGS = ['-std=c++0x']+     LDFLAGS = []++   Run the following commands::++     > python setup.py build_ext --compiler=mingw32+     > python setup.py install++If everything went in order, pyOpenCL should be installed on your machine now.++10) Lastly, Pygame needs to be installed.  Again, not stricly+    necessary, but some examples make use of it.  To do so on Windows,+    download ``pygame-1.9.2a0-cp27-none-win_amd64.whl`` from `here+    <http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame>`_. ``cp27``+    means Python 2.7 and ``win_amd64`` means 64-bit Windows.++    Go to the directory you have downloaded the file and execute ``pip+    install pygame-1.9.2a0-cp27-none-win_amd64.whl`` from the command+    line.++Now you should be able to run the `Game of Life`_ example.++11) To run the makefiles, first setup ``make`` by going to the ``bin``+    directory of MingWpy and making a copy of+    ``mingw32-make.exe``. Then simply rename ``mingw32-make –+    Copy.exe`` or similar to ``make.exe``. Now you will be able to run+    the makefiles.++    Also, if you have not selected to add the optional Linux command+    line tools to ``PATH`` during the ``Git for Windows``+    installation, add the ``C:\Program Files\Git\usr\bin`` directory+    to ``PATH`` manually now.++12) This guide has been written off memory, so if you are having+    difficulties - ask on the `issues page`_. There might be errors in+    it.++.. _`PyOpenCL repository`: https://github.com/pyopencl/pyopencl+.. _`Game of Life`: https://github.com/diku-dk/futhark-benchmarks/tree/master/misc/life+.. _`issues page`: https://github.com/diku-dk/futhark/issues
+ docs/language-reference.rst view
@@ -0,0 +1,1567 @@+.. _language-reference:++Language Reference+==================++This reference seeks to describe every construct in the Futhark+language.  It is not presented in a tutorial fashion, but rather+intended for quick lookup and documentation of subtleties.  For this+reason, it is not written in a bottom-up manner, and some concepts may+be used before they are fully defined.  It is a good idea to have a+basic grasp of Futhark (or some other functional programming language)+before reading this reference.  An ambiguous grammar is given for the+full language.  The text describes how ambiguities are resolved in+practice (for example by applying rules of operator precedence).++This reference describes only the language itself.  Documentation for+the basis library is `available elsewhere+<https://futhark-lang.org/docs/>`_.++Identifiers and Keywords+------------------------++.. productionlist::+   id: `letter` (`letter` | "_" | "'")* | "_" `id`+   quals: (`id` ".")++   qualid: `id` | `quals` `id`+   binop: `opstartchar` `opchar`*+   qualbinop: `binop` | `quals` `binop` | "`" `qualid` "`"+   fieldid: `decimal` | `id`+   opstartchar: "+" | "-" | "*" | "/" | "%" | "=" | "!" | ">" | "<" | "|" | "&" | "^"+   opchar: `opstartchar` | "."+   constructor: "#" `id`++Many things in Futhark are named. When we are defining something, we+give it an unqualified name (`id`).  When referencing something inside+a module, we use a qualified name (`qualid`).  The constructor names+of a sum type (:ref:`compounds`) are identifiers prefixed with ``#``,+with no space afterwards.  The fields of a record are named with+`fieldid`.  Note that a `fieldid` can be a decimal number.  Futhark+has three distinct name spaces: terms, module types, and types.+Modules (including parametric modules) and values both share the term+namespace.++.. _primitives:++Primitive Types and Values+--------------------------++.. productionlist::+   literal: `intnumber` | `floatnumber` | "true" | "false"++Boolean literals are written ``true`` and ``false``.  The primitive+types in Futhark are the signed integer types ``i8``, ``i16``,+``i32``, ``i64``, the unsigned integer types ``u8``, ``u16``, ``u32``,+``u64``, the floating-point types ``f32``, ``f64``, as well as+``bool``.  An ``f32`` is always a single-precision float and a ``f64``+is a double-precision float.++.. productionlist::+   int_type: "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64"+   float_type: "f32" | "f64"++Numeric literals can be suffixed with their intended type.  For+example ``42i8`` is of type ``i8``, and ``1337e2f64`` is of type+``f64``.  If no suffix is given, the type of the literal will be+inferred based on its use.  If the use is not constrained, integral+literals will be assigned type ``i32``, and decimal literals type+``f64``.  Hexadecimal literals are supported by prefixing with ``0x``,+and binary literals by prefixing with ``0b``.++Floats can also be written in hexadecimal format such as ``0x1.fp3``,+instead of the usual decimal notation. Here, ``0x1.f`` evaluates to+``1 15/16`` and the ``p3`` multiplies it by ``2^3 = 8``.++.. productionlist::+   intnumber: (`decimal` | `hexadecimal` | `binary`) [`int_type`]+   decimal: `decdigit` (`decdigit` |"_")*+   hexadecimal: 0 ("x" | "X") `hexdigit` (`hexdigit` |"_")*+   binary: 0 ("b" | "B") `bindigit` (`bindigit` | "_")*++.. productionlist::+   floatnumber: (`pointfloat` | `exponentfloat`) [`float_type`]+   pointfloat: [`intpart`] `fraction`+   exponentfloat: (`intpart` | `pointfloat`) `exponent`+   hexadecimalfloat: 0 ("x" | "X") `hexintpart` `hexfraction` ("p"|"P") ["+" | "-"] `decdigit`++   intpart: `decdigit` (`decdigit` |"_")*+   fraction: "." `decdigit` (`decdigit` |"_")*+   hexintpart: `hexdigit` (`hexdigit` | "_")*+   hexfraction: "." `hexdigit` (`hexdigit` |"_")*+   exponent: ("e" | "E") ["+" | "-"] `decdigit`+++.. productionlist::+   decdigit: "0"..."9"+   hexdigit: `decdigit` | "a"..."f" | "A"..."F"+   bindigit: "0" | "1"++.. _compounds:++Compound Types and Values+~~~~~~~~~~~~~~~~~~~~~~~~~++.. productionlist::+   type:   `qualid`+       : | `array_type`+       : | `tuple_type`+       : | `record_type`+       : | `sum_type`+       : | `function_type`+       : | `type_application`++Compound types can be constructed based on the primitive types.  The+Futhark type system is entirely structural, and type abbreviations are+merely shorthands.  The only exception is abstract types whose+definition has been hidden via the module system (see `Module+System`_).++.. productionlist::+   tuple_type: "(" ")" | "(" `type` ("[" "," `type` "]")* ")"++A tuple value or type is written as a sequence of comma-separated+values or types enclosed in parentheses.  For example, ``(0, 1)`` is a+tuple value of type ``(i32,i32)``.  The elements of a tuple need not+have the same type -- the value ``(false, 1, 2.0)`` is of type+``(bool, i32, f64)``.  A tuple element can also be another tuple, as+in ``((1,2),(3,4))``, which is of type ``((i32,i32),(i32,i32))``.  A+tuple cannot have just one element, but empty tuples are permitted,+although they are not very useful.  Empty tuples are written ``()``+and are of type ``()``.++.. productionlist::+   array_type: "[" [`dim`] "]" `type`+   dim: `qualid` | `decimal`++An array value is written as a sequence of zero or more+comma-separated values enclosed in square brackets: ``[1,2,3]``.  An+array type is written as ``[d]t``, where ``t`` is the element type of+the array, and ``d`` is an integer or variable indicating the size.+We can often elide ``d`` and write just ``[]`` (an *anonymous size*),+in which case the size will be inferred.  As an example, an array of+three integers could be written as ``[1,2,3]``, and has type+``[3]i32``.  An empty array is written as ``[]``, and its type is+inferred from its use.  When writing Futhark values for such uses as+``futhark test`` (but not when writing programs), empty arrays are+written ``empty([0]t)`` for an empty array of type ``[0]t``.  When+using ``empty``, all dimensions must be given a size, and at least one+must be zero, e.g. ``empty([2][0]i32)``.++Multi-dimensional arrays are supported in Futhark, but they must be+*regular*, meaning that all inner arrays must have the same shape.+For example, ``[[1,2], [3,4], [5,6]]`` is a valid array of type+``[3][2]i32``, but ``[[1,2], [3,4,5], [6,7]]`` is not, because there+we cannot come up with integers ``m`` and ``n`` such that+``[m][n]i32`` describes the array.  The restriction to regular arrays+is rooted in low-level concerns about efficient compilation.  However,+we can understand it in language terms by the inability to write a+type with consistent dimension sizes for an irregular array value.  In+a Futhark program, all array values, including intermediate (unnamed)+arrays, must be typeable.++.. productionlist::+   sum_type: `constructor` `type`* ("|" `constructor` `type`*)*++Sum types are anonymous in Futhark, and are written as the+constructors separated by vertical bars.  Each constructor consists of+a ``#``-prefixed *name*, followed by zero or more types, called its+*payload*.  **Note:** The current implementation of sum types is+fairly inefficient, in that all possible constructors of a sum-typed+value will be resident in memory.  Avoid using sum types where+multiple constructors have large payloads.++.. productionlist::+   record_type: "{" "}" | "{" `fieldid` ":" `type` ("," `fieldid` ":" `type`)* "}"++Records are mappings from field names to values, with the field names+known statically.  A tuple behaves in all respects like a record with+numeric field names starting from zero, and vice versa.  It is an+error for a record type to name the same field twice.++.. productionlist::+   type_application: `type` `type_arg` | "*" `type`+   type_arg: "[" [`dim`] "]" | `type`++A parametric type abbreviation can be applied by juxtaposing its name+and its arguments.  The application must provide as many arguments as+the type abbreviation has parameters - partial application is+presently not allowed.  See `Type Abbreviations`_ for further details.++.. productionlist::+   function_type: `param_type` "->" `type`+   param_type: `type` | "(" `id` ":" `type` ")"++Functions are classified via function types, but they are not fully+first class.  See :ref:`hofs` for the details.++.. productionlist::+   stringlit: '"' `stringchar` '"'+   stringchar: <any source character except "\" or newline or quotes>++String literals are supported, but only as syntactic sugar for UTF-8+encoded arrays of ``u8`` values.  There is no character type in+Futhark.++Declarations+------------++A Futhark file or module consists of a sequence of declarations.  Each+declaration is processed in order, and a declaration can only refer to+names bound by preceding declarations.++.. productionlist::+   dec:   `fun_bind` | `val_bind` | `type_bind` | `mod_bind` | `mod_type_bind`+      : | "open" `mod_exp`+      : | "import" `stringlit`+      : | "local" `dec`++The ``open`` declaration brings names defined in another module into+scope (see also `Module System`_).  For the meaning of ``import``, see+`Referring to Other Files`_.  If a declaration is prefixed with+``local``, whatever names it defines will *not* be visible outside the+current module.  In particular ``local open`` is used to bring names+from another module into scope, without making those names available+to users of the module being defined.  In most cases, using module+type ascription is a better idea.++Declaring Functions and Values+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++.. productionlist::+   fun_bind:   ("let" | "entry") (`id` | "(" `binop` ")") `type_param`* `pat`+ [":" `type`] "=" `exp`+           : | ("let" | "entry") `pat` `binop` `pat` [":" `type`] "=" `exp`++.. productionlist::+   val_bind: "let" `id` [":" `type`] "=" `exp`++Functions and values must be defined before they are used.  A function+declaration must specify the name, parameters, and body+of the function::++  let name params...: rettype = body++Hindley-Milner-style type inference is supported.  A parameter may be+given a type with the notation ``(name: type)``.  Functions may not be+recursive.  You may put *size annotations* in the return type and+parameter types; see `Size Types`_.  A function can be *polymorphic*+by using type parameters, in the same way as for `Type+Abbreviations`_::++  let reverse [n] 't (xs: [n]t): [n]t = xs[::-1]++Type parameters for a function do not need to cover the types of all+parameters.  The type checker will add more if necessary.  For+example, the following is well typed::++  let pair 'a (x: a) y = (x, y)++A new type variable will be invented for the parameter ``y``.++Shape and type parameters are not passed explicitly when calling+function, but are automatically derived.  If an array value *v* is+passed for a type parameter *t*, all other arguments passed of type+*t* must have the same shape as *v*.  For example, consider the following+definition::++  let pair 't (x: t) (y: t) = (x, y)++The application ``pair [1] [2,3]`` will fail at run-time.++To simplify the handling of in-place updates (see+:ref:`in-place-updates`), the value returned by a function may not+alias any global variables.++User-Defined Operators+~~~~~~~~~~~~~~~~~~~~~~++Infix operators are defined much like functions::++  let (p1: t1) op (p2: t2): rt = ...++For example::++  let (a:i32,b:i32) +^ (c:i32,d:i32) = (a+c, b+d)++We can also define operators by enclosing the operator name in+parentheses and suffixing the parameters, as an ordinary function::++  let (+^) (a:i32,b:i32) (c:i32,d:i32) = (a+c, b+d)++This is necessary when defining a polymorphic operator.++A valid operator name is a non-empty sequence of characters chosen+from the string ``"+-*/%=!><&^"``.  The fixity of an operator is+determined by its first characters, which must correspond to a+built-in operator.  Thus, ``+^`` binds like ``+``, whilst ``*^`` binds+like ``*``.  The longest such prefix is used to determine fixity, so+``>>=`` binds like ``>>``, not like ``>``.++It is not permitted to define operators with the names ``&&`` or+``||`` (although these as prefixes are accepted).  This is because a+user-defined version of these operators would not be short-circuiting.+User-defined operators behave exactly like ordinary functions, except+for being infix.++A built-in operator can be shadowed (i.e. a new ``+`` can be defined).+This will result in the built-in polymorphic operator becoming+inaccessible, except through the ``intrinsics`` module.++An infix operator can also be defined with prefix notation, like an+ordinary function, by enclosing it in parentheses::++  let (+) (x: i32) (y: i32) = x - y++This is necessary when defining operators that take type or shape+parameters.++.. _entry-points:++Entry Points+~~~~~~~~~~~~++Apart from declaring a function with the keyword ``let``, it can also+be declared with ``entry``.  When the Futhark program is compiled any+top-level function declared with ``entry`` will be exposed as an entry+point.  If the Futhark program has been compiled as a library, these+are the functions that will be exposed.  If compiled as an executable,+you can use the ``--entry-point`` command line option of the generated+executable to select the entry point you wish to run.++Any top-level function named ``main`` will always be considered an+entry point, whether it is declared with ``entry`` or not.++Value Declarations+~~~~~~~~~~~~~~~~~~++A named value/constant can be declared as follows::++  let name: type = definition++The definition can be an arbitrary expression, including function+calls and other values, although they must be in scope before the+value is defined.  A constant value may not have a unique type (see+`In-place updates`_).  If the return type contains any anonymous sizes+(see `Size types`_), new existential sizes will be constructed for+them.++Type Abbreviations+~~~~~~~~~~~~~~~~~~++.. productionlist::+   type_bind: "type" ["^" | "~"] `id` `type_param`* "=" `type`+   type_param: "[" `id` "]" | "'" `id` | "'~" `id` | "'^" `id`++Type abbreviations function as shorthands for the purpose of+documentation or brevity.  After a type binding ``type t1 = t2``, the+name ``t1`` can be used as a shorthand for the type ``t2``.  Type+abbreviations do not create distinct types: the types ``t1`` and+``t2`` are entirely interchangeable.++If the right-hand side of a type contains anonymous sizes, it must be+declared "size-lifted" with ``type~``.  If it (potentially) contains a+function, it must be declared "fully lifted" with ``type^``.  A lifted+type can also contain anonymous sizes.  Lifted types cannot be put in+arrays.  Fully lifted types cannot be returned from conditional or+loop expressions.++A type abbreviation can have zero or more parameters.  A type+parameter enclosed with square brackets is a *size parameter*, and+can be used in the definition as an array dimension size, or as a+dimension argument to other type abbreviations.  When passing an+argument for a shape parameter, it must be enclosed in square+brackets.  Example::++  type two_intvecs [n] = ([n]i32, [n]i32)++  let x: two_intvecs [2] = (iota 2, replicate 2 0)++Size parameters work much like shape declarations for arrays.  Like+shape declarations, they can be elided via square brackets containing+nothing.  All size parameters must be used in the definition of the+type abbreviation.++A type parameter prefixed with a single quote is a *type parameter*.+It is in scope as a type in the definition of the type abbreviation.+Whenever the type abbreviation is used in a type expression, a type+argument must be passed for the parameter.  Type arguments need not be+prefixed with single quotes::++  type two_vecs [n] 't = ([n]t, [n]t)+  type two_intvecs [n] = two_vecs [n] i32+  let x: two_vecs [2] i32 = (iota 2, replicate 2 0)++A *size-lifted type parameter* is prefixed with ``'~``, and a *fully+lifted type parameter* with ``'^``.  These have the same rules and+restrictions as lifted type abbreviations.++Expressions+-----------++Expressions are the basic construct of any Futhark program.  An+expression has a statically determined *type*, and produces a *value*+at runtime.  Futhark is an eager/strict language ("call by value").++The basic elements of expressions are called *atoms*, for example+literals and variables, but also more complicated forms.++.. productionlist::+   atom:   `literal`+       : | `qualid` ("." `fieldid`)*+       : | `stringlit`+       : | "(" ")"+       : | "(" `exp` ")" ("." `fieldid`)*+       : | "(" `exp` ("," `exp`)* ")"+       : | "{" "}"+       : | "{" field ("," `field`)* "}"+       : | `qualid` "[" `index` ("," `index`)* "]"+       : | "(" `exp` ")" "[" `index` ("," `index`)* "]"+       : | `quals` "." "(" `exp` ")"+       : | "[" `exp` ("," `exp`)* "]"+       : | "[" `exp` [".." `exp`] "..." `exp` "]"+       : | "(" `qualbinop` ")"+       : | "(" `exp` `qualbinop` ")"+       : | "(" `qualbinop` `exp` ")"+       : | "(" ( "." `field` )+ ")"+       : | "(" "." "[" `index` ("," `index`)* "]" ")"+   exp:   `atom`+      : | `exp` `qualbinop` `exp`+      : | `exp` `exp`+      : | `constructor` `exp`*+      : | `exp` ":" `type`+      : | `exp` ":>" `type`+      : | `exp` [ ".." `exp` ] "..." `exp`+      : | `exp` [ ".." `exp` ] "..<" `exp`+      : | `exp` [ ".." `exp` ] "..>" `exp`+      : | "if" `exp` "then" `exp` "else" `exp`+      : | "let" `pat` "=" `exp` "in" `exp`+      : | "let" `id` "[" `index` ("," `index`)* "]" "=" `exp` "in" `exp`+      : | "let" `id` `type_param`* `pat`+ [":" `type`] "=" `exp` "in" `exp`+      : | "(" "\" `pat`+ [":" `type`] "->" `exp` ")"+      : | "loop" `pat` [("=" `exp`)] `loopform` "do" `exp`+      : | "unsafe" `exp`+      : | "assert" `atom` `atom`+      : | `exp` "with" "[" `index` ("," `index`)* "]" "=" `exp`+      : | `exp` "with" `fieldid` ("." `fieldid`)* "=" `exp`+      : | "match" `exp` ("case" `pat` "->" `exp`)++   field:   `fieldid` "=" `exp`+        : | `id`+   pat:   `id`+      : | `literal`+      : |  "_"+      : | "(" ")"+      : | "(" `pat` ")"+      : | "(" `pat` ("," `pat`)+ ")"+      : | "{" "}"+      : | "{" `fieldid` ["=" `pat`] ["," `fieldid` ["=" `pat`]] "}"+      : | `constructor` `pat`*+      : | `pat` ":" `type`+   loopform :   "for" `id` "<" `exp`+            : | "for" `pat` "in" `exp`+            : | "while" `exp`+   index:   `exp` [":" [`exp`]] [":" [`exp`]]+        : | [`exp`] ":" `exp` [":" [`exp`]]+        : | [`exp`] [":" `exp`] ":" [`exp`]++Some of the built-in expression forms have parallel semantics, but it+is not guaranteed that the the parallel constructs in Futhark are+evaluated in parallel, especially if they are nested in complicated+ways.  Their purpose is to give the compiler as much freedom and+information is possible, in order to enable it to maximise the+efficiency of the generated code.++Resolving Ambiguities+~~~~~~~~~~~~~~~~~~~~~++The above grammar contains some ambiguities, which in the concrete+implementation is resolved via a combination of lexer and grammar+transformations.  For ease of understanding, they are presented here+in natural text.++* An expression ``x.y`` may either be a reference to the name ``y`` in+  the module ``x``, or the field ``y`` in the record ``x``.  Modules+  and values occupy the same name space, so this is disambiguated by+  the type of ``x``.++* A type ascription (``exp : type``) cannot appear as an array+  index, as it conflicts with the syntax for slicing.++* In ``f [x]``, there is am ambiguity between indexing the array ``f``+  at position ``x``, or calling the function ``f`` with the singleton+  array ``x``.  We resolve this the following way:++    * If there is a space between ``f`` and the opening bracket, it is+      treated as a function application.++    * Otherwise, it is an array index operation.++* An expression ``(-x)`` is parsed as the variable ``x`` negated and+  enclosed in parentheses, rather than an operator section partially+  applying the infix operator ``-``.++* The following table describes the precedence and associativity of+  infix operators.  All operators in the same row have the same+  precedence.  The rows are listed in increasing order of precedence.+  Note that not all operators listed here are used in expressions;+  nevertheless, they are still used for resolving ambiguities.++  =================  =============+  **Associativity**  **Operators**+  =================  =============+  left               ``,``+  left               ``:``, ``:>``+  left               ``||``+  left               ``&&``+  left               ``<=`` ``>=`` ``>`` ``<`` ``==`` ``!=``+  left               ``&`` ``^`` ``|``+  left               ``<<`` ``>>``+  left               ``+`` ``-``+  left               ``*`` ``/`` ``%`` ``//`` ``%%``+  left               ``|>``+  right              ``<|``+  right              ``->``+  left               juxtaposition+  =================  =============++.. _patterns:++Patterns+~~~~~~~~++We say that a pattern is *irrefutable* if it can never fail to match a+value of the appropriate type.  Concretely, this means that it does+not require any specific sum type constructor (unless the type in+question has only a single constructor), or any specific numeric or+boolean literal.  Patterns used in function parameters and ``let``+bindings must be irrefutable.  Patterns used in ``case`` need not be+irrefutable.++A pattern ``_`` matches any value.  A pattern consisting of a literal+value (e.g. a numeric constant) matches exactly that value.++Semantics of Simple Expressions+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++`literal`+.........++Evaluates to itself.++`qualid`+........++A variable name; evaluates to its value in the current environment.++`stringlit`+...........++Evaluates to an array of type ``[]i32`` that contains the code points+of the characters as integers.++``()``+......++Evaluates to an empty tuple.++``( e )``+.........++Evaluates to the result of ``e``.++``(e1, e2, ..., eN)``+.....................++Evaluates to a tuple containing ``N`` values.  Equivalent to the+record literal ``{0=e1, 1=e2, ..., N-1=eN}``.++``{f1, f2, ..., fN}``+.....................++A record expression consists of a comma-separated sequence of *field+expressions*.  Each field expression defines the value of a field in+the record.  A field expression can take one of two forms:++  ``f = e``: defines a field with the name ``f`` and the value+  resulting from evaluating ``e``.++  ``f``: defines a field with the name ``f`` and the value of the+  variable ``f`` in scope.++Each field may only be defined once.++``a[i]``+........++Return the element at the given position in the array.  The index may+be a comma-separated list of indexes instead of just a single index.+If the number of indices given is less than the rank of the array, an+array is returned.++The array ``a`` must be a variable name or a parenthesised expression.+Furthermore, there *may not* be a space between ``a`` and the opening+bracket.  This disambiguates the array indexing ``a[i]``, from ``a+[i]``, which is a function call with a literal array.++.. _slices:++``a[i:j:s]``+............++Return a slice of the array ``a`` from index ``i`` to ``j``, the+former inclusive and the latter exclusive, taking every ``s``-th+element.  The ``s`` parameter may not be zero.  If ``s`` is negative,+it means to start at ``i`` and descend by steps of size ``s`` to ``j``+(not inclusive).++It is generally a bad idea for ``s`` to be non-constant.+Slicing of multiple dimensions can be done by separating with commas,+and may be intermixed freely with indexing.++If ``s`` is elided it defaults to ``1``.  If ``i`` or ``j`` is elided, their+value depends on the sign of ``s``.  If ``s`` is positive, ``i`` become ``0``+and ``j`` become the length of the array.  If ``s`` is negative, ``i`` becomes+the length of the array minus one, and ``j`` becomes minus one.  This means that+``a[::-1]`` is the reverse of the array ``a``.++In the general case, the size of the array produced by a slice is+unknown (see `Size types`_).  In a few cases, the size is known+statically:++  * ``a[0:n]`` has size ``n``++  * ``a[:n]`` has size ``n``++  * ``a[0:n:1]`` has size ``n``++  * ``a[:n:1]`` has size ``n``++This holds only if ``n`` is a variable or constant.++``[x, y, z]``+.............++Create an array containing the indicated elements.  Each element must+have the same type and shape.++.. _range:++``x..y...z``+............++Construct an integer array whose first element is ``x`` and which+proceeds stride of ``y-x`` until reaching ``z`` (inclusive).  The+``..y`` part can be elided in which case a stride of 1 is used.  A+run-time error occurs if ``z`` is lesser than ``x`` or ``y``, or if+``x`` and ``y`` are the same value.++In the general case, the size of the array produced by a range is+unknown (see `Size types`_).  In a few cases, the size is known+statically:++  * ``1..2...n`` has size ``n``++This holds only if ``n`` is a variable or constant.++.. _range_upto:++``x..y..<z``+............++Construct an integer array whose first elements is ``x``, and which+proceeds upwards with a stride of ``y`` until reaching ``z``+(exclusive).  The ``..y`` part can be elided in which case a stride of+1 is used.  A run-time error occurs if ``z`` is lesser than ``x`` or+``y``, or if ``x`` and ``y`` are the same value.++  * ``0..1..<n`` has size ``n``++  * ``0..<n`` has size ``n``++This holds only if ``n`` is a variable or constant.++``x..y..>z``+...............++Construct an integer array whose first elements is ``x``, and which+proceeds downwards with a stride of ``y`` until reaching ``z``+(exclusive).  The ``..y`` part can be elided in which case a stride of+-1 is used.  A run-time error occurs if ``z`` is greater than ``x`` or+``y``, or if ``x`` and ``y`` are the same value.++``e.f``+........++Access field ``f`` of the expression ``e``, which must be a record or+tuple.++``m.(e)``+.........++Evaluate the expression ``e`` with the module ``m`` locally opened, as+if by ``open``.  This can make some expressions easier to read and+write, without polluting the global scope with a declaration-level+``open``.++``x`` *binop* ``y``+...................++Apply an operator to ``x`` and ``y``.  Operators are functions like+any other, and can be user-defined.  Futhark pre-defines certain+"magical" *overloaded* operators that work on many different types.+Overloaded functions cannot be defined by the user.  Both operands+must have the same type.  The predefined operators and their semantics+are:++  ``**``++    Power operator, defined for all numeric types.++  ``//``, ``%%``++    Division and remainder on integers, with rounding towards zero.++  ``*``, ``/``, ``%``, ``+``, ``-``++    The usual arithmetic operators, defined for all numeric types.+    Note that ``/`` and ``%`` rounds towards negative infinity when+    used on integers - this is different from in C.++  ``^``, ``&``, ``|``, ``>>``, ``<<``++    Bitwise operators, respectively bitwise xor, and, or, arithmetic+    shift right and left, and logical shift right.  Shift amounts+    must be non-negative and the operands must be integers.  Note+    that, unlike in C, bitwise operators have *higher* priority than+    arithmetic operators.  This means that ``x & y == z`` is+    understood as ``(x & y) == z``, rather than ``x & (y == z)`` as+    it would in C.  Note that the latter is a type error in Futhark+    anyhow.++  ``==``, ``!=``++      Compare any two values of builtin or compound type for equality.++  ``<``, ``<=``.  ``>``, ``>=``++      Company any two values of numeric type for equality.++``x && y``+..........++Short-circuiting logical conjunction; both operands must be of type+``bool``.++``x || y``+..........++Short-circuiting logical disjunction; both operands must be of type+``bool``.++``f x``+.......++Apply the function ``f`` to the argument ``x``.++``#c x y z``+............++Apply the sum type constructor ``#x`` to the payload ``x``, ``y``, and+``z``.  A constructor application is always assumed to be saturated,+i.e. its entire payload provided.  This means that constructors may+not be partially applied.++``e : t``+.........++Annotate that ``e`` is expected to be of type ``t``, failing with a+type error if it is not.  If ``t`` is an array with shape+declarations, the correctness of the shape declarations is checked at+run-time.++Due to ambiguities, this syntactic form cannot appear as an array+index expression unless it is first enclosed in parentheses.  However,+as an array index must always be of type ``i32``, there is never a+reason to put an explicit type ascription there.++``e :> t``+..........++Coerce the size of ``e`` to ``t``.  The type of ``t`` must match the+type of ``e``, except that the sizes may be statically different.  At+run-time, it will be verified that the sizes are the same.++``! x``+.......++Logical negation if ``x`` is of type ``bool``.  Bitwise negation if+``x`` is of integral type.++``- x``+.......++Numerical negation of ``x``, which must be of numeric type.++``unsafe e``+............++Elide safety checks and assertions (such as bounds checking) that+occur during execution of ``e``.  This is useful if the compiler is+otherwise unable to avoid bounds checks (e.g. when using indirect+indexes), but you really do not want them there.  Make very sure that+the code is correct; eliding such checks can lead to memory+corruption.++``assert cond e``+.................++Terminate execution with an error if ``cond`` evaluates to false,+otherwise produce the result of evaluating ``e``.  Unless ``e``+produces a value that is used subsequently (it can just be a+variable), dead code elimination may remove the assertion.++``a with [i] = e``+...................++Return ``a``, but with the element at position ``i`` changed to+contain the result of evaluating ``e``.  Consumes ``a``.++``r with f = e``+.................++Return the record ``r``, but with field ``f`` changed to have value+``e``.  The type of the field must remain unchanged.  Type inference+is limited: ``r`` must have a *completely known type* up to ``f``.+This sometimes requires extra type annotations to make the type of+``r`` known.++``if c then a else b``+......................++If ``c`` evaluates to ``true``, evaluate ``a``, else evaluate ``b``.++Binding Expressions+~~~~~~~~~~~~~~~~~~~++``let pat = e in body``+.......................++Evaluate ``e`` and bind the result to the irrefutable pattern ``pat``+(see :ref:`patterns`) while evaluating ``body``.  The ``in`` keyword+is optional if ``body`` is a ``let`` expression.++``let a[i] = v in body``+........................................++Write ``v`` to ``a[i]`` and evaluate ``body``.  The given index need+not be complete and can also be a slice, but in these cases, the value+of ``v`` must be an array of the proper size.  This notation is+Syntactic sugar for ``let a = a with [i] = v in a``.++``let f params... = e in body``+...............................++Bind ``f`` to a function with the given parameters and definition+(``e``) and evaluate ``body``.  The function will be treated as+aliasing any free variables in ``e``.  The function is not in scope of+itself, and hence cannot be recursive.++``loop pat = initial for x in a do loopbody``+.............................................++1. Bind ``pat`` to the initial values given in ``initial``.++2. For each element ``x`` in ``a``, evaluate ``loopbody`` and rebind+   ``pat`` to the result of the evaluation.++3. Return the final value of ``pat``.++The ``= initial`` can be left out, in which case initial values for+the pattern are taken from equivalently named variables in the+environment.  I.e., ``loop (x) = ...`` is equivalent to ``loop (x = x)+= ...``.++``loop pat = initial for x < n do loopbody``+............................................++Equivalent to ``loop (pat = initial) for x in [0..1..<n] do loopbody``.++``loop pat = initial = while cond do loopbody``+...............................................++1. Bind ``pat`` to the initial values given in ``initial``.++2. If ``cond`` evaluates to true, bind ``pat`` to the result of+   evaluating ``loopbody``, and repeat the step.++3. Return the final value of ``pat``.++``match x case p1 -> e1 case p2 -> e2``+.......................................++Match the value produced by ``x`` to each of the patterns in turn,+picking the first one that succeeds.  The result of the corresponding+expression is the value of the entire ``match`` expression.  All the+expressions associated with a ``case`` must have the same type (but+not necessarily match the type of ``x``).  It is a type error if there+is not a ``case`` for every possible value of ``x`` - inexhaustive+pattern matching is not allowed.++Function Expressions+~~~~~~~~~~~~~~~~~~~~++``\x y z: t -> e``+..................++Produces an anonymous function taking parameters ``x``, ``y``, and+``z``, returns type ``t``, and whose body is ``e``.  Lambdas do not+permit type parameters; use a named function if you want a polymorphic+function.++``(binop)``+...........++An *operator section* that is equivalent to ``\x y -> x *binop* y``.++``(x binop)``+.............++An *operator section* that is equivalent to ``\y -> x *binop* y``.++``(binop y)``+.............++An *operator section* that is equivalent to ``\x -> x *binop* y``.++``(.a.b.c)``+............++An *operator section* that is equivalent to ``\x -> x.a.b.c``.++``(.[i,j])``+............++An *operator section* that is equivalent to ``\x -> x[i,j]``.++.. _hofs:++Higher-order functions+----------------------++At a high level, Futhark functions are values, and can be used as any+other value.  However, to ensure that the compiler is able to compile+the higher-order functions efficiently via *defunctionalisation*,+certain type-driven restrictions exist on how functions can be used.+These also apply to any record or tuple containing a function (a+*functional type*):++* Arrays of functions are not permitted.++* A function cannot be returned from an ``if`` expression.++* A ``loop`` parameter cannot be a function.++Further, *type parameters* are divided into *non-lifted* (bound with+an apostrophe, e.g. ``'t``), *size-lifted* (``'~t``), and *fully+lifted* (``'^t``).  Only fully lifted type parameters may be+instantiated with a functional type.  Within a function, a lifted type+parameter is treated as a functional type.++See also `In-place updates`_ for details on how uniqueness types+interact with higher-order functions.++Type Inference+--------------++Futhark supports Hindley-Milner-style type inference, so in many cases+explicit type annotations can be left off.  Record field projection+cannot in isolation be fully inferred, and may need type annotations+where their inputs are bound.  The same goes when constructing sum+types, as Futhark cannot assume that a given constructor only belongs+to a single type.  Further, unique types (see `In-place updates`_)+must be explicitly annotated.++.. _size-types:++Size Types+----------++Futhark supports a simple system of size-dependent types that+statically verifies that the sizes of arrays passed to a function are+compatible.  The focus is on simplicity, not completeness.++Whenever a pattern occurs (in ``let``, ``loop``, and function+parameters), as well as in return types, *size annotations* may be+used to express invariants about the shapes of arrays that are+accepted or produced by the function.  For example::++  let f [n] (a: [n]i32) (b: [n]i32): [n]i32 =+    map (+) a b++We use a *size parameter*, ``[n]``, to explicitly quantify the names+of shapes.  The ``[n]`` parameter is not explicitly passed when+calling ``f``.  Rather, its value is implicitly deduced from the+arguments passed for the value parameters.  An array can contain+*anonymous dimensions*, e.g. ``[]i32``, for which the type checker+will invent fresh size parameters, which ensures that all sizes have a+(symbolic) size.++A size annotation can also be an integer constant (with no suffix).+Size parameters can be used as ordinary variables within the scope of+the parameters.  The type checker verifies that the program obeys any+constraints imposed by size annotations.++*Size-dependent types* are supported, as the names of parameters can+be used in the return type of a function::++  let replicate 't (n: i32) (x: t): [n]t = ...++An application ``replicate 10 0`` will have type ``[10]i32``.++.. _unknown-sizes:++Unknown sizes+~~~~~~~~~~~~~++Since sizes must be constants or variables, there are many cases where+the type checker cannot assign a precise size to the result of some+operation.  For example, the type of ``concat`` should conceptually be::++  val concat [n] [m] 't : [n]t -> [m]t -> [n+m]t++But this is not presently allowed.  Instead, the return type contains+an anonymous size::++  val concat [n] [m] 't : [n]t -> [m]t -> []t++When an application ``concat xs ys`` is found, the result will be of+type ``[k]t``, where ``k`` is a fresh *unknown* size variable that is+considered distinct from every other size in the program.  It is often+necessary to perform a size coercion (see `Size coercion`_) to+convert an unknown size to a known size.++Generally, unknown sizes are constructed whenever the true size cannot+be expressed.  The following lists all possible sources of unknown+sizes.++Size going out of scope+.......................++An unknown size is created when the proper size of an array refers to+a name that has gone out of scope::++  let c = a + b+  in replicate c 0++The type of ``replicate c 0`` is ``[c]i32``, but since ``c`` is+locally bound, the type of the entire expression is ``[k]i32`` for+some fresh ``k``.++Compound expression passed as function argument+...............................................++Intuitively, the type of ``replicate (x+y) 0`` should be ``[x+y]i32``,+but since sizes must be names or constants, this is not expressible.+Therefore an unknown size variable is created and the size of the+expression becomes ``[k]i32``.++Compound expression used as range bound+.......................................++While a simple range expression such as ``0..<n`` can be assigned type+``[n]i32``, a range expression ``0..<(n+1)`` will give produce an+unknown size.++Complex slicing+...............++Most complex array slicing, such as ``xs[a:b]``, will have an unknown+size.  Exceptions are listed in the :ref:`reference for slice+expressions <slices>`.++Complex ranges+..............++Most complex ranges, such as ``a..<b``, will have an known size.+Exceptions exist for :ref:`general ranges <range>` and :ref:`"upto"+ranges <range_upto>`.++Anonymous size in function return type+......................................++Whenever the result of a function application would have an anonymous+size, that size is replaced with a fresh unknown size variable.++For example, ``filter`` has the following type::++  val filter [n] 'a : (p: a -> bool) -> (as: [n]a) -> []a++Naively, an application ``filter f xs`` seems like it would have type+``[]a``, but a fresh unknown size ``k`` will be created and the actual+type will be ``[k]a``.++Branches of ``if`` return arrays of different sizes+...................................................++When an ``if`` (or ``match``) expression has branches that returns+array of different sizes, the differing sizes will be replaced with+fresh unknown sizes.  For example::++  if b then [[1,2], [3,4]]+       else [[5,6]]++This expression will have type ``[k][2]i32``, for some fresh ``k``.++**Important:** The check whether the sizes differ is done when first+encountering the ``if`` or ``match`` during type checking.  At this+point, the type checker may not realise that the two sizes are+actually equal, even though constraints later in the function force+them to be.  This can always be resolved by adding type annotations.++An array produced by a loop does not have a known size+......................................................++If the size of some loop parameter is not maintained across a loop+iteration, the final result of the loop will contain unknown sizes.+For example::++  loop xs = [1] for i < n do xs ++ xs++Similar to conditionals, the type checker may sometimes be too+cautious in assuming that some size may change during the loop.+Adding type annotations to the loop parameter can be used to resolve+this.++Size coercion+~~~~~~~~~~~~~++Size coercion, written with ``:>``, can be used to perform a+runtime-checked coercion of one size to another.  Since size+annotations can refer only to variables and constants, this is+necessary when writing more complicated size functions::++  let concat_to 'a (m: i32) (a: []a) (b: []a) : [m]a =+    a ++ b :> [m]a++Only expression-level type annotations give rise to run-time checks.+Despite their similar syntax, parameter and return type annotations+must be valid at compile-time, or type checking will fail.++Causality restriction+~~~~~~~~~~~~~~~~~~~~~++Conceptually, size parameters are assigned their value by reading the+sizes of concrete values passed along as parameters.  This means that+any size parameter must be used as the size of some parameter.  This+is an error::++  let f [n] (x: i32) = n++The following is not an error::++  let f [n] (g: [n]i32 -> [n]i32) = ...++However, using this function comes with a constraint: whenever an+application ``f x`` occurs, the value of the size parameter must be+inferable.  Specifically, this value must have been used as the size+of an array *before* the ``f x`` application is encountered.  The+notion of "before" is subtle, as there is no evaluation ordering of a+Futhark expression, *except* that a ``let``-binding is always+evaluated before its body, and the argument to a function is always+evaluated before the function.++The causality restriction only occurs when a function has size+parameters whose first use is *not* as a concrete array size.  For+example, it does not apply to uses of the following function::++  let f [n] (arr: [n]i32) (g: [n]i32 -> [n]i32) = ...++This is because the proper value of ``n`` can be read directly from+the actual size of the array.++Empty array literals+~~~~~~~~~~~~~~~~~~~~++Just as with size-polymorphic functions, when constructing an empty+array, we must know the exact size of the (missing) elements.  For+example, in the following program we are forcing the elements of ``a``+to be the same as the elements of ``b``, but the size of the elements+of ``b`` are not known at the time ``a`` is constructed::++  let main (b: bool) (xs: []i32) =+    let a = [] : [][]i32+    let b = [filter (>0) xs]+    in a[0] == b[0]++The result is a type error.++Sum types+~~~~~~~~~++When constructing a value of a sum type, the compiler must still be+able to determine the size of the constructors that are *not* used.+This is illegal::++  type sum = #foo ([]i32) | #bar ([]i32)++  let main (xs: *[]i32) =+    let v : sum = #foo xs+    in xs++Modules+~~~~~~~++When matching a module with a module type (see :ref:`module-system`),+a non-lifted abstract type (i.e. one that is declared with ``type``+rather than ``type^``) may not be implemented by a type abbreviation+that contains any anonymous sizes.  This is to ensure that if we have+the following::++  module m : { type t } = ...++Then we can construct an array of values of type ``m.t`` without+worrying about constructing an irregular array.++Higher-order functions+~~~~~~~~~~~~~~~~~~~~~~++When a higher-order function takes a functional argument whose return+type is a non-lifted type parameter, any instantiation of that type+parameter must have a non-anonymous size.  If the return type is a+lifted type parameter, then the instantiation may contain anonymous+sizes.  This is why the type of ``map`` guarantees regular arrays::++  val map [n] 'a 'b : (a -> b) -> [n]a -> [n]b++The type parameter ``b`` can only be replaced with a type that has+non-anonymous sizes, which means they must be the same for every+application of the function.  In contrast, this is the type of the+pipeline operator::++  val (|>) '^a -> '^b : a -> (a -> b) -> b++The provided function can return something with an anonymous size+(such as ``filter``).++A function whose return type has an unknown size+................................................++If a function (named or anonymous) is inferred to have a return type+that contains an unknown size variable created *within* the function+body, that size variable will be replaced with an anonymous size.  In+most cases this is not important, but it means that an expression like+the following is ill-typed::++  map (\xs -> iota (length xs)) (xss : [n][m]i32)++This is because the ``(length xs)`` expression gives rise to some+fresh size ``k``.  The lambda is then assigned the type ``[n]t ->+[k]i32``, which is immediately turned into ``[n]t -> []i32`` because+``k`` was generated inside its body.  A function of this type cannot+be passed to ``map``, as explained before.  The solution is to bind+``length`` to a name *before* the lambda.++.. _in-place-updates:++In-place Updates+----------------++In-place updates do not provide observable side effects, but they do+provide a way to efficiently update an array in-place, with the+guarantee that the cost is proportional to the size of the value(s)+being written, not the size of the full array.++The ``a with [i] = v`` language construct, and derived forms,+performs an in-place update.  The compiler verifies that the original+array (``a``) is not used on any execution path following the in-place+update.  This involves also checking that no *alias* of ``a`` is used.+Generally, most language constructs produce new arrays, but some+(slicing) create arrays that alias their input arrays.++When defining a function parameter or return type, we can mark it as+*unique* by prefixing it with an asterisk.  For example::++  let modify (a: *[]i32) (i: i32) (x: i32): *[]i32 =+    a with [i] = a[i] + x++For bulk in-place updates with multiple values, use the ``scatter``+function in the basis library.  In the parameter declaration ``a:+*[i32]``, the asterisk means that the function ``modify`` has been+given "ownership" of the array ``a``, meaning that any caller of+``modify`` will never reference array ``a`` after the call again.+This allows the ``with`` expression to perform an in-place update.++After a call ``modify a i x``, neither ``a`` or any variable that+*aliases* ``a`` may be used on any following execution path.++Alias Analysis+~~~~~~~~~~~~~~++The rules used by the Futhark compiler to determine aliasing are+intuitive in the intra-procedural case.  Aliases are associated with+entire arrays.  Aliases of a record are tuple are tracked for each+element, not for the record or tuple itself.  Most constructs produce+fresh arrays, with no aliases.  The main exceptions are ``if``,+``loop``, function calls, and variable literals.++* After a binding ``let a = b``, that simply assigns a new name to an+  existing variable, the variable ``a`` aliases ``b``.  Similarly for+  record projections and patterns.++* The result of an ``if`` aliases the union of the aliases of the+  components.++* The result of a ``loop`` aliases the initial values, as well as any+  aliases that the merge parameters may assume at the end of an+  iteration, computed to a fixed point.++* The aliases of a value returned from a function is the most+  interesting case, and depends on whether the return value is+  declared *unique* (with an asterisk ``*``) or not.  If it is+  declared unique, then it has no aliases.  Otherwise, it aliases all+  arguments passed for *non-unique* parameters.++In-place Updates and Higher-Order Functions+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++Uniqueness typing generally interacts poorly with higher-order+functions.  The issue is that we cannot control how many times a+function argument is applied, or to what, so it is not safe to pass a+function that consumes its argument.  The following two conservative+rules govern the interaction between uniqueness types and higher-order+functions:++1. In the expression ``let p = e1 in ...``, if *any* in-place update+   takes place in the expression ``e1``, the value bound by ``p`` must+   not be or contain a function.++2. A function that consumes one of its arguments may not be passed as+   a higher-order argument to another function.++.. _module-system:++Module System+-------------++.. productionlist::+   mod_bind: "module" `id` `mod_param`* "=" [":" mod_type_exp] "=" `mod_exp`+   mod_param: "(" `id` ":" `mod_type_exp` ")"+   mod_type_bind: "module" "type" `id` `type_param`* "=" `mod_type_exp`++Futhark supports an ML-style higher-order module system.  *Modules*+can contain types, functions, and other modules and module types.+*Module types* are used to classify the contents of modules, and+*parametric modules* are used to abstract over modules (essentially+module-level functions).  In Standard ML, modules, module types and+parametric modules are called structs, signatures, and functors,+respectively.  Module names exist in the same name space as values,+but module types are their own name space.++Named modules are declared as::++  module name = ...++A named module type is defined as::++  module type name = ...++Where a module expression can be the name of another module, an+application of a parametric module, or a sequence of declarations+enclosed in curly braces::++  module Vec3 = {+    type t = ( f32 , f32 , f32 )+    let add(a: t) (b: t): t =+      let (a1, a2, a3) = a in+      let (b1, b2, b3) = b in+      (a1 + b1, a2 + b2 , a3 + b3)+  }++  module AlsoVec3 = Vec3++Functions and types within modules can be accessed using dot+notation::++    type vector = Vec3.t+    let double(v: vector): vector = Vec3.add v v++We can also use ``open Vec3`` to bring the names defined by ``Vec3``+into the current scope.  Multiple modules can be opened simultaneously+by separating their names with spaces.  In case several modules define+the same names, the ones mentioned last take precedence.  The first+argument to ``open`` may be a full module expression.++Named module types are defined as::++  module type ModuleTypeName = ...++A module type expression can be the name of another module type, or a+sequence of *specifications*, or *specs*, enclosed in curly braces.  A+spec can be a *value spec*, indicating the presence of a function or+value, an *abstract type spec*, or a *type abbreviation spec*.  For+example::++  module type Addable = {+    type t                 -- abstract type spec+    type two_ts = (t,t)    -- type abbreviation spec+    val add: t -> t -> t   -- value spec+  }++This module type specifies the presence of an *abstract type* ``t``,+as well as a function operating on values of type ``t``.  We can use+*module type ascription* to restrict a module to what is exposed by+some module type::++  module AbstractVec = Vec3 : Addable++The definition of ``AbstractVec.t`` is now hidden.  In fact, with this+module type, we can neither construct values of type ``AbstractVec.T``+or convert them to anything else, making this a rather useless use of+abstraction.  As a derived form, we can write ``module M: S = e`` to+mean ``module M = e : S``.++In a value spec, sizes in types on the left-hand side of a function+arrow must not be anonymous.  For example, this is forbidden::++  val sum: []t -> t++Instead write::++  val sum [n]: [n]t -> t++But this is allowed, because the empty size is not to the left of a+function arrow::++  val evens [n]: [n]i32 -> []i32++Parametric modules allow us to write definitions that abstract over+modules.  For example::++  module Times = \(M: Addable) -> {+    let times (x: M.t) (k: int): M.t =+      loop (x' = x) for i < k do+        T.add x' x+  }++We can instantiate ``Times`` with any module that fulfils the module+type ``Addable`` and get back a module that defines a function+``times``::++  module Vec3Times = Times Vec3++Now ``Vec3Times.times`` is a function of type ``Vec3.t -> int ->+Vec3.t``.  As a derived form, we can write ``module M p = e`` to mean+``module M = \p -> e``.++Module Expressions+~~~~~~~~~~~~~~~~~~++.. productionlist::+   mod_exp:   `qualid`+          : | `mod_exp` ":" `mod_type_exp`+          : | "\" "(" `id` ":" `mod_type_exp` ")" [":" `mod_type_exp`] "->" `mod_exp`+          : | `mod_exp` `mod_exp`+          : | "(" `mod_exp` ")"+          : | "{" `dec`* "}"+          : | "import" `stringlit`++A module expression produces a module.  Modules are collections of+bindings produced by declarations (`dec`).  In particular, a module+may contain other modules or module types.++``qualid``+..........++Evaluates to the module of the given name.++``(mod_exp)``+.............++Evaluates to ``mod_exp``.++``mod_exp : mod_type_exp``+..........................++*Module ascription* evaluates the module expression and the module+type expression, verifies that the module implements the module type,+then returns a module that exposes only the functionality described by+the module type.  This is how internal details of a module can be+hidden.++``\(p: mt1): mt2 -> e``+.......................++Constructs a *parametric module* (a function at the module level) that+accepts a parameter of module type ``mt1`` and returns a module of+type ``mt2``.  The latter is optional, but the parameter type is not.++``e1 e2``+.........++Apply the parametric module ``m1`` to the module ``m2``.++``{ decs }``+............++Returns a module that contains the given definitions.  The resulting+module defines any name defined by any declaration that is not+``local``, *in particular* including names made available via+``open``.++``import "foo"``+................++Returns a module that contains the definitions of the file ``"foo"``+relative to the current file.  See :ref:`other-files`.++Module Type Expressions+~~~~~~~~~~~~~~~~~~~~~~~++.. productionlist::+   mod_type_exp:   `qualid`+             : | "{" `spec`* "}"+             : | `mod_type_exp` "with" `qualid` `type_param`* "=" `type`+             : | "(" `mod_type_exp` ")"+             : | "(" `id` ":" `mod_type_exp` ")" "->" `mod_type_exp`+             : | `mod_type_exp` "->" `mod_type_exp`+++.. productionlist::+   spec:   "val" `id` `type_param`* ":" `spec_type`+       : | "val" `binop` `type_param`* ":" `spec_type`+       : | "type" ["^"] `id` `type_param`* "=" `type`+       : | "type" ["^"] `id` `type_param`*+       : | "module" `id` ":" `mod_type_exp`+       : | "include" `mod_type_exp`+   spec_type: `type` | `type` "->" `spec_type`++Module types classify modules, with the only (unimportant) difference+in expressivity being that modules can contain module types, but+module types cannot specify that a module must contain a specific+module type. They can specify of course that a module contains a+*submodule* of a specific module type.++.. _other-files:++Referring to Other Files+------------------------++You can refer to external files in a Futhark file like this::++  import "file"++The above will include all non-``local`` top-level definitions from+``file.fut`` is and make them available in the current file (but+will not export them).  The ``.fut`` extension is implied.++You can also include files from subdirectories::++  import "path/to/a/file"++The above will include the file ``path/to/a/file.fut`` relative to the+including file.++Qualified imports are also possible, where a module is created for the+file::++  module M = import "file"++In fact, a plain ``import "file"`` is equivalent to::++  local open import "file"
docs/man/futhark-bench.rst view
@@ -76,6 +76,10 @@      futhark bench prog.fut --backend=opencl --pass-option=-dHawaii +--pass-compiler-option=opt++  Pass an extra option to the compiler when compiling the programs.+ --runner=program    If set to a non-empty string, compiled programs are not run
docs/man/futhark-c.rst view
@@ -51,6 +51,9 @@ -V   Print version information on standard output and exit. +-W+  Do not print any warnings.+ --Werror   Treat warnings as errors. 
docs/man/futhark-csharp.rst view
@@ -49,6 +49,9 @@   error, the result of the last successful compiler step will be   printed to standard error. +-W+  Do not print any warnings.+ -V   Print version information on standard output and exit. 
docs/man/futhark-csopencl.rst view
@@ -51,6 +51,9 @@ -V   Print version information on standard output and exit. +-W+  Do not print any warnings.+ --Werror   Treat warnings as errors. 
docs/man/futhark-cuda.rst view
@@ -53,6 +53,9 @@ -V   Print version information on standard output and exit. +-W+  Do not print any warnings.+ --Werror   Treat warnings as errors. 
docs/man/futhark-opencl.rst view
@@ -53,6 +53,9 @@ -V   Print version information on standard output and exit. +-W+  Do not print any warnings.+ --Werror   Treat warnings as errors. 
docs/man/futhark-pyopencl.rst view
@@ -60,6 +60,9 @@ -V   Print version information on standard output and exit. +-W+  Do not print any warnings.+ --Werror   Treat warnings as errors. 
docs/man/futhark-python.rst view
@@ -54,6 +54,9 @@ -V   Print version information on standard output and exit. +-W+  Do not print any warnings.+ --Werror   Treat warnings as errors. 
docs/man/futhark-test.rst view
@@ -160,7 +160,11 @@   Pass an option to benchmark programs that are being run.  For   example, we might want to run OpenCL programs on a specific device:: -    futhark-bench prog.fut --backend=opencl --pass-option=-dHawaii+    futhark test prog.fut --backend=opencl --pass-option=-dHawaii++--pass-compiler-option=opt++  Pass an extra option to the compiler when compiling the programs.  --runner=program 
docs/man/futhark.rst view
@@ -24,10 +24,11 @@ COMMANDS ======== -futhark check PROGRAM----------------------+futhark check [-w] PROGRAM+-------------------------- -Check whether a Futhark program type checks.+Check whether a Futhark program type checks.  With ``-w``, no warnings+are printed.  futhark datacmp FILE_A FILE_B -----------------------------
+ docs/package-management.rst view
@@ -0,0 +1,361 @@+.. _package-management:++Package Management+==================++This document describes ``futhark pkg``, a minimalistic package+manager inspired by `vgo <https://research.swtch.com/vgo>`_.  A+Futhark package is a downloadable collection of ``.fut`` files and+little more.  There is a (not necessarily comprehensive) `list of+known packages <https://futhark-lang.org/pkgs>`_.++Basic Concepts+--------------++A package is uniquely identified with a *package path*, which is+similar to a URL, except without a protocol.  At the moment, package+paths are always links to Git repositories hosted on GitHub or GitLab.+In the future, this will become more flexible.  As an example, a+package path may be ``github.com/athas/fut-foo``.++Packages are versioned with `semantic version numbers+<https://semver.org/>`_ of the form ``X.Y.Z``.  Whenever versions are+indicated, all three digits must always be given (that is, ``1.0`` is+not a valid shorthand for ``1.0.0``).++Most ``futhark pkg`` operations involve reading and writing a *package+manifest*, which is always stored in a file called ``futhark.pkg``.+The ``futhark.pkg`` file is human-editable, but is in day-to-day use+mainly modified by ``futhark pkg`` automatically.++Using Packages+--------------++Required packages can be added by using ``futhark pkg add``, for example::++  $ futhark pkg add github.com/athas/fut-foo 0.1.0++This will create a new file ``futhark.pkg`` with the following contents:++.. code-block:: text++   require {+     github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa733+   }++This lists one required package, with its package path, minimum+version (see :ref:`version-selection`), and the expected commit hash.+The latter is used for verification, to ensure that the contents of a+package version cannot be changed silently.++``futhark pkg`` will perform network requests to determine whether a+package of the given name and with the given version exists and fail+otherwise (but it will not check whether the package is otherwise+well-formed).  The version number can be elided, in which case+``futhark pkg`` will use the newest available version.  If the package+is already present in ``futhark.pkg``, it will simply have its version+requirement changed to the one specified in the command.  Any+dependencies of the package will *not* be added to ``futhark.pkg``,+but will still be downloaded by ``futhark pkg sync`` (see below).++Adding a package with ``futhark pkg add`` modifies ``futhark.pkg``,+but does not download the package files.  This is done with+``futhark pkg sync`` (without further options).  The contents of each+required dependency and any transitive dependencies will be stored in+a subdirectory of ``lib/`` corresponding to their package path.  As an+example::++  $ futhark pkg sync+  $ tree lib+  lib+  └── github.com+      └── athas+          └── fut-foo+              └── foo.fut++  3 directories, 1 file++**Warning:** ``futhark pkg sync`` will remove any unrecognized files or+local modifications to files in ``lib/`` (except of course the package+directory of the package path listed in ``futhark.pkg``; see+:ref:`creating-packages`).++Packages can be removed from ``futhark.pkg`` with::++  $ futhark pkg remove pkgpath++You will need to run ``futhark pkg sync`` to actually remove the files in+``lib/``.++The intended usage is that ``futhark.pkg`` is added to version+control, but ``lib/`` is not, as the contents of ``lib/`` can always+be reproduced from ``futhark.pkg``.  However, adding ``lib/`` works+just fine as well.++Importing Files from Dependencies+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++``futhark pkg sync`` will populate the ``lib/`` directory, but does+not interact with the compiler in any way.  The downloaded files can+be imported using the usual ``import`` mechanism (:ref:`other-files`);+for example, assuming the package contains a file ``foo.fut``::++  import "lib/github.com/athas/fut-foo/foo"++Ultimately, everything boils down to ordinary file system semantics.+This has the downside of relatively long and clumsy import paths, but+the upside of predictability.++Upgrading Dependencies+~~~~~~~~~~~~~~~~~~~~~~++The ``futhark pkg upgrade`` command will update every version+requirement in ``futhark.pkg`` to be the most recent available+version.  You still need to run ``futhark pkg sync`` to actually+retrieve the new versions.  Be careful - while upgrades are safe if+semantic versioning is followed correctly, this is not yet properly+machine-checked, so human mistakes may occur.++As an example:++.. code-block:: text++   $ cat futhark.pkg+   require {+     github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa733+   }+   $ futhark pkg upgrade+   Upgraded github.com/athas/fut-foo 0.1.0 => 0.2.1.+   $ cat futhark.pkg+   require {+     github.com/athas/fut-foo 0.2.1 #3ddc9fc93c1d8ce560a3961e55547e5c78bd0f3e+   }+   $ futhark pkg sync+   $ tree lib+   lib+   └── github.com+       └── athas+           ├── fut-bar+           │   └── bar.fut+           └── fut-foo+               └── foo.fut++   4 directories, 2 files++Note that ``fut-foo 0.2.1`` depends on ``github.com/athas/fut-bar``,+so it was fetched by ``futhark pkg sync``.++``futhark pkg upgrade`` will *never* upgrade across a major version+number.  Due to the principle of `Semantic Import Versioning+<https://research.swtch.com/vgo-import>`_, a new major version is a+completely different package from the point of view of the package+manager.  Thus, to upgrade to a new major version, you will need to+use ``futhark pkg add`` to add the new version and ``futhark pkg+remove`` to remove the old version.  Or you can keep it around - it is+perfectly acceptable to depend on multiple major versions of the same+package, because they are really different packages.++.. _creating-packages:++Creating Packages+-----------------++A package is a directory tree (which at the moment must correspond to+a Git repository).  It *must* contain two things:++  * A file ``futhark.pkg`` at the root defining the package path and+    any required packages.++  * A *package directory* ``lib/pkg-path``, where ``pkg-path`` is the+    full package path.++The contents of the package directory is what will be made available+to users of the package.  The repository may contain other things+(tests, data files, examples, docs, other programs, etc), but these+are ignored by ``futhark pkg``.  This structure can be created+automatically by running for example::++  $ futhark pkg init github.com/sturluson/edda++Note again, no ``https://``.  The result is this ``futhark.pkg``::++  package github.com/sturluson/edda++  require {+  }++And this file hierarchy:++.. code-block:: text++   $ tree lib+   lib+   └── github.com+       └── sturluson+           └── edda++   3 directories, 0 files++Note that ``futhark pkg init`` is not necessary simply to *use*+packages, only when *creating* packages.++When creating a package, the ``.fut`` files we are writing will be+located inside the ``lib/`` directory.  If the package has its own+dependencies, whose files we would like to access, we can use+*relative imports*.  For example, assume we are creating a package+``github.com/sturluson/edda`` and we are writing a Futhark file+located at ``lib/github.com/sturluson/edda/saga.fut``.  Further, we+have a dependency on the package ``github.com/athas/foo-fut``, which+is stored in the directory ``lib/github.com/athas/foo-fut``.  We can+import a file ``lib/github.com/athas/foo-fut/foo.fut`` from+``lib/github.com/sturluson/edda/saga.fut`` with::++  import "../foo-fut/foo"++Releasing a Package+~~~~~~~~~~~~~~~~~~~++Currently, a package corresponds exactly to a GitHub repository+mirroring the package path.  A release is done by tagging an+appropriate commit with ``git tag vX.Y.Z`` and then pushing the tag to+GitHub with ``git push --tags``.  In the future, this will be+generalised to other code hosting sites and version control systems+(and possibly self-hosted tarballs).  Remember to take semantic+versioning into account - unless you bump the major version number (or+the major version is 0), the new version must be *fully compatible*+with the old.++When releasing a new package, consider getting it added to the+`central package list <https://futhark-lang.org/pkgs>`_.  See `this+page+<https://github.com/diku-dk/futhark-docbot/blob/master/README.md>`_+for details.++Incrementing the Major Version Number+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++While backwards-incompatible modifications to a package are sometimes+unavoidable, it is wise to avoid them as much as possible, as they+significantly inconvenience users.  To discourage breaking+compatibility, ``futhark pkg`` tries to ensure that the package+developer feels this inconvenience as well.  In many cases, an+incompatible change can be avoided simply by adding new files to the+package rather than incompatibly changing the existing ones.++In the general case, the package path also encodes the major version+of the package, separated with a ``@``.  For example, version 5.2.1 of+a package might have the package path ``github.com/user/repo@5``.  For+major versions 0 and 1, this can be elided.  This means that multiple+(major) versions of a package are completely distinct from the point+of view of the package manager - this principle is called `Semantic+Import Versioning <https://research.swtch.com/vgo-import>`_, and is+intended to facilitate backwards compatibility of packages when new+versions are released.++If you really must increment the major version, then you will need to+change the package path in ``futhark.pkg`` to contain the new major+version preceded by ``@``.  For example,+``lib/github.com/sturluson/edda`` becomes+``lib/github.com/sturluson/edda@2``.  As a special case, this is not+necessary when moving from major version 0 to 1.  Since the package+path has changed, you will also need to rename the package directory+in ``lib/``.  This is painful and awkward, but it is less painful and+awkward than what users feel when their dependencies break+compatibility.++Renaming a Package+~~~~~~~~~~~~~~~~~~++It is likely that the hosting location for a very long-lived package+will change from time to time.  Since the hosting location is embedded+into the package path itself, this causes some issues for+``futhark pkg``.++In simple cases, there is no problem.  Consider a package+``github.com/asgard/loki`` which is moved to+``github.com/utgard/loki``.  If no GitHub-level redirect is set up,+all users must update the path by which they import the package.  This+is unavoidable, unfortunately.++However, the old tagged versions, which contain a ``futhark.pkg`` that+uses the old package path, will continue to work.  This is because the+package path indicated in ``package.pkg`` merely defines the+subdirectory of ``lib/`` where the package files are to be found,+while the package path used by dependents in the ``require`` section+defines where the package files are located after ``futhark pkg+sync``.  Thus, when we import an old version of+``github.com/utgard/loki`` whose ``futhark.pkg`` defines the package+as ``github.com/asgard/loki``, the package files will be retrieved+from the ``lib/github.com/asgard/loki`` directory in the repository,+but stored at ``lib/github.com/utgard/loki`` in the local directory.++The above means that package management remains operational in simple+cases of renaming, but it is awkward when a transitive dependency is+renamed (or deleted).  The Futhark package ecosystem is sufficiently+embryonic that we have not yet developed more robust solutions.  When+such solutions are developed, they will likely involve some form of+``replace`` directive that allows transparent local renaming of+packages, as well as perhaps a central registry of package paths that+does not depend on specific source code hosts.++.. _version-selection:++Version Selection+-----------------++The package manifest ``futhark.pkg`` declares which packages the+program depends on.  Dependencies are specified as the *oldest+acceptable version* within the given major version.  Upper version+bounds are not supported, as strict adherence to semantic versioning+is assumed, so any later version with the same major version number+should work.  When ``futhark pkg sync`` calculates which version of a+given package to download, it will pick the oldest version that still+satisfies the minimum version requirements of that package in all+transitive dependencies.  This means that a version may be used that+is newer than the one indicated in ``futhark.pkg``, but only if a+dependency requires a more recent version.++Tests and Documentation for Dependencies+----------------------------------------++Package management has been designed to ensure that the normal+development tools work as expected with the contents of the ``lib/``+directory.  For example, to ensure that all dependencies do in fact+work well (or at least compile) together, run:++.. code-block:: text++   futhark test lib++Also, you can generate hyperlinked documentation for all dependencies+with:++.. code-block:: text++   futhark doc lib -o docs++The file ``docs/index.html`` can be opened in a web browser to browse+the documentation.  Prebuilt documentation is also available via the+`online package list <https://futhark-lang.org/pkgs>`_.++Safety+------++In contrast to some other package managers, ``futhark pkg`` does not+run any package-supplied code on installation, upgrade, or removal.+This means that all ``futhark pkg`` operations are in principle+completely safe (barring exploitable bugs in ``futhark pkg`` itself,+which is unlikely but not impossible).  Further, Futhark code itself+is also completely pure, so executing it cannot have any unfortunate+effects, such as `infecting all of your own packages with a worm+<https://jamie.build/how-to-build-an-npm-worm>`_.  The worst it can do+is loop infinitely, consume arbitrarily large amounts of memory, or+produce wrong results.++The exception is packages that uses ``unsafe``.  With some cleverness,+``unsafe`` can be combined with in-place updates to perform arbitrary+memory reads and writes, which can trivially lead to exploitable+behaviour.  You should not use untrusted code that employs ``unsafe``+(but the ``--safe`` compiler option may help).  However, this is not+any worse than calling external code in a conventional impure+language, which generally can perform any conceivable harmful action.
+ docs/requirements.txt view
@@ -0,0 +1,1 @@+pyyaml>=4.2b1
+ docs/usage.rst view
@@ -0,0 +1,457 @@+.. _usage:++Basic Usage+===========++Futhark contains several code generation backends.  Each is provided+as subcommand of the ``futhark`` binary.  For example, ``futhark c``+compiles a Futhark program by translating it to sequential C code,+while ``futhark pyopencl`` generates Python code with calls to the+PyOpenCL library.  The different compilers all contain the same+frontend and optimisation pipeline - only the code generator is+different.  They all provide roughly the same command line interface,+but there may be minor differences and quirks due to characteristics+of the specific backends.++There are two main ways of compiling a Futhark program: to an+executable (by using ``--executable``, which is the default), and to a+library (``--library``).  Executables can be run immediately, but are+useful mostly for testing and benchmarking.  Libraries can be called+from non-Futhark code.++Compiling to Executable+-----------------------++A Futhark program is stored in a file with the extension ``.fut``.  It+can be compiled to an executable program as follows::++  $ futhark c prog.fut++This makes use of the ``futhark c`` compiler, but any other will work+as well.  The compiler will automatically invoke ``gcc`` to produce an+executable binary called ``prog``.  If we had used ``futhark py``+instead of ``futhark c``, the ``prog`` file would instead have+contained Python code, along with a `shebang`_ for easy execution.  In+general, when compiling file ``foo.fut``, the result will be written+to a file ``foo`` (i.e. the extension will be stripped off).  This can+be overridden using the ``-o`` option.  For more details on specific+compilers, see their individual manual pages.++.. _shebang: https://en.wikipedia.org/wiki/Shebang_%28Unix%29++Executables generated by the various Futhark compilers share a common+command-line interface, but may also individually support more+options.  When a Futhark program is run, execution starts at one of+its *entry points*.  By default, the entry point named ``main`` is+run.  An alternative entry point can be indicated by using the ``-e``+option.  All entry point functions must be declared appropriately in+the program (see :ref:`entry-points`).  If the entry point takes any+parameters, these will be read from standard input in a subset of the+Futhark syntax.  A binary input format is also supported; see+:ref:`binary-data-format`.  The result of the entry point is printed+to standard output.++Only a subset of all Futhark values can be passed to an executable.+Specifically, only primitives and arrays of primitive types are+supported.  In particular, nested tuples and arrays of tuples are not+permitted.  Non-nested tuples are supported are supported as simply+flat values.  This restriction is not present for Futhark programs+compiled to libraries.  If an entry point *returns* any such value,+its printed representation is unspecified.  As a special case, an+entry point is allowed to return a flat tuple.++Instead of compiling, there is also an interpreter, accessible as+``futhark run`` and ``futhark repl``.  The latter is an interactive+prompt, useful for experimenting with Futhark expressions.  Be aware+that the interpreter runs code very slowly.+++.. _executable-options:++Executable Options+^^^^^^^^^^^^^^^^^^++All generated executables support the following options.++  ``-t FILE``++    Print the time taken to execute the program to the indicated file,+    an integral number of microseconds.  The time taken to perform setup+    or teardown, including reading the input or writing the result, is+    not included in the measurement.  See the documentation for specific+    compilers to see exactly what is measured.++  ``-r RUNS``++    Run the specified entry point the given number of times (plus a+    warmup run).  The program result is only printed once, after the+    last run.  If combined with ``-t``, one measurement is printed per+    run.  This is a good way to perform benchmarking.++  ``-D``++    Print debugging information on standard error.  Exactly what is+    printed, and how it looks, depends on which Futhark compiler is+    used.  This option may also enable more conservative (and slower)+    execution, such as frequently synchronising to check for errors.++  ``-b``++    Print the result using the binary data format+    (:ref:`binary-data-format`).  For large outputs, this is+    significantly faster and takes up less space.++Parallel Options+~~~~~~~~~~~~~~~~++The following options are supported by executables generated with the+parallel backends (``opencl``, ``pyopencl``, ``csopencl``, and+``cuda``).++  ``--tuning=FILE``++    Load tuning options from the indicated *tuning file*.  The file+    must contain lines of the form ``SIZE=VALUE``, where each *SIZE*+    must be one of the sizes listed by the ``--print-sizes`` option+    (without size class), and the *VALUE* must be a non-negative+    integer.  Extraneous spaces or blank lines are not allowed.  A zero+    means to use the default size, whatever it may be.  In case of+    duplicate assignments to the same size, the last one takes+    predecence.  This is equivalent to passing each size setting on+    the command like using the ``--size`` option, but more convenient.++  ``--print-sizes``++    Print a list of tunable sizes followed by their *size class* in+    parentheses, which indicates what they are used for.++  ``--size=SIZE=VALUE``++    Set one of the tunable sizes to the given value.  Using the+    ``--tuning`` option is more convenient.++OpenCL-specific Options+~~~~~~~~~~~~~~~~~~~~~~~++The following options are supported by executables generated with the+OpenCL backends (``opencl``, ``pyopencl``, and ``csopencl``):++  ``-P``++    Measure the time taken by various OpenCL operations (such as+    kernels) and print a summary at the end.  Unfortunately, it is+    currently nontrivial (and manual) to relate these operations back+    to source Futhark code.++  ``-p PLATFORM``++    Pick the first OpenCL platform whose name contains the given+    string.  The special string ``#k``, where ``k`` is an integer, can+    be used to pick the *k*-th platform, numbered from zero.++  ``-d DEVICE``++    Pick the first OpenCL device whose name contains the given string.+    The special string ``#k``, where ``k`` is an integer, can be used+    to pick the *k*-th device, numbered from zero.  If used in+    conjunction with ``-p``, only the devices from matching platforms+    are considered.++  ``--default-group-size INT``++    The default size of OpenCL workgroups that are launched.  Capped+    to the hardware limit if necessary.++  ``--default-num-groups INT``++    The default number of OpenCL workgroups that are launched.++  ``--dump-opencl FILE``++    Don't run the program, but instead dump the embedded OpenCL+    program to the indicated file.  Useful if you want to see what is+    actually being executed.++  ``--load-opencl FILE``++    Instead of using the embedded OpenCL program, load it from the+    indicated file.  This is extremely unlikely to result in succesful+    execution unless this file is the result of a previous call to+    ``--dump-opencl`` (perhaps lightly modified).++  ``--dump-opencl-binary FILE``++    Don't run the program, but instead dump the compiled version of+    the embedded OpenCL program to the indicated file.  On NVIDIA+    platforms, this will be PTX code.  If this option is set, no entry+    point will be run.++  ``--load-opencl-binary FILE``++    Load an OpenCL binary from the indicated file.++  ``--build-option OPT``++    Add an additional build option to the string passed to+    ``clBuildProgram()``.  Refer to the OpenCL documentation for which+    options are supported.  Be careful - some options can easily+    result in invalid results.++There is rarely a need to use both ``-p`` and ``-d``.  For example, to+run on the first available NVIDIA GPU, ``-p NVIDIA`` is sufficient, as+there is likely only a single device associated with this platform.+On \*nix (including macOS), the `clinfo+<https://github.com/Oblomov/clinfo>`_ tool (available in many package+managers) can be used to determine which OpenCL platforms and devices+are available on a given system.  On Windows, `CPU-z+<https://www.cpuid.com/softwares/cpu-z.html>`_ can be used.++CUDA-specific Options+~~~~~~~~~~~~~~~~~~~~~++The following options are supported by executables generated by the+``cuda`` backend:++  ``--dump-cuda FILE``++    Don't run the program, but instead dump the embedded CUDA program+    to the indicated file.  Useful if you want to see what is actually+    being executed.++  ``--load-cuda FILE``++    Instead of using the embedded CUDA program, load it from the+    indicated file.  This is extremely unlikely to result in succesful+    execution unless this file is the result of a previous call to+    ``--dump-cuda`` (perhaps lightly modified).++  ``--dump-ptx FILE``++    As ``--dump-cuda``, but dumps the compiled PTX code instead.++  ``--load-ptx FILE``++    Instead of using the embedded CUDA program, load compiled PTX code+    from the indicated file.++  ``--nvrtc-option=OPT``++    Add the given option to the command line used to compile CUDA+    kernels with NVRTC.  The list of supported options varies with the+    CUDA version but can be `found in the NVRTC+    documentation+    <https://docs.nvidia.com/cuda/nvrtc/index.html#group__options>`_.++For convenience, CUDA executables also accept the same+``--default-num-groups`` and ``--default-group-size`` options that the+OpenCL backend uses.  These then refer to grid size and thread block+size, respectively.++Compiling to Library+--------------------++While compiling a Futhark program to an executable is useful for+testing, it is not suitable for production use.  Instead, a Futhark+program should be compiled into a reusable library in some target+language, enabling integration into a larger program.  Five of the+Futhark compilers support this: ``futhark c``, ``futhark opencl``,+``futhark cuda``, ``futhark py``, and ``futhark pyopencl``.++General Concerns+^^^^^^^^^^^^^^^^++Futhark entry points are mapped to some form of function or method in+the target language.  Generally, an entry point taking *n* parameters+will result in a function taking *n* parameters.  Extra parameters may+be added to pass in context data, or *out*-parameters for writing the+result, for target languages that do not support multiple return+values from functions.++Not all Futhark types can be mapped cleanly to the target language.+Arrays of tuples, for example, are a common issue.  In such cases, *opaque+types* are used in the generated code.  Values of these types cannot+be directly inspected, but can be passed back to Futhark entry points.+In the general case, these types will be named with a random hash.+However, if you insert an explicit type annotation (and the type+name contains only characters valid for identifiers for the used+backend), the indicated name will be used.  Note that arrays contain+brackets, which are usually not valid in identifiers.  Defining a+simple type alias is the best way around this.++Generating C+^^^^^^^^^^^^++A Futhark program ``futlib.fut`` can be compiled to reusable C code+using either::++  $ futhark c --library futlib.fut++Or::++  $ futhark opencl --library futlib.fut++This produces two files in the current directory: ``futlib.c`` and+``futlib.h``.  If we wish (and are on a Unix system), we can then+compile ``futlib.c`` to an object file like this::++  $ gcc futlib.c -c++This produces a file ``futlib.o`` that can then be linked with the+main application.  Details of how to link the generated code with+other C code is highly system-dependent, and outside the scope of this+manual.  On Unix, we can simply add ``futlib.o`` to the final compiler+or linker command line::++  $ gcc main.c -o main futlib.o++Depending on the Futhark backend you are using, you may need to add+some linker flags.  For example, ``futhark opencl`` requires+``-lOpenCL`` (``-framework OpenCL`` on macOS).  See the manual page+for each compiler for details.++It is also possible to simply add the generated ``.c`` file to the C+compiler command line used for compiling our whole program (here+``main.c``)::++  $ gcc main.c -o main futlib.c++The downside of this approach is that the generated ``.c`` file may+contain code that causes the C compiler to warn (for example, unused+support code that is not needed by the Futhark program).++The generated header file (here, ``futlib.h``) specifies the API, and+is intended to be human-readable.  The basic usage revolves around+creating a *configuration object*, which can then be used to obtain a+*context object*, which must be passed whenever entry points are+called.++The configuration object is created using the following function::++  struct futhark_context_config *futhark_context_config_new();++Depending on the backend, various functions are generated to modify+the configuration.  The following is always available::++  void futhark_context_config_set_debugging(struct futhark_context_config *cfg,+                                            int flag);++A configuration object can be used to create a context with the+following function::++  struct futhark_context *futhark_context_new(struct futhark_context_config *cfg);++Memory management is entirely manual.  Deallocation functions are+provided for all types defined in the header file.  Everything+returned by an entry point must be manually deallocated.++Functions that can fail return an integer: 0 on success and a non-zero+value on error.  A human-readable string describing the error can be+retrieved with the following function::++  char *futhark_context_get_error(struct futhark_context *ctx);++It is the caller's responsibility to ``free()`` the returned string.+Any subsequent call to the function returns ``NULL``, until a new+error occurs.++For now, many internal errors, such as failure to allocate memory,+will cause the function to ``abort()`` rather than return an error+code.  However, all application errors (such as bounds and array size+checks) will produce an error code.++The API functions are thread safe.++C with OpenCL+~~~~~~~~~~~~~++When generating C code with ``futhark opencl``, you will need to link+against the OpenCL library when linking the final binary::++  $ gcc main.c -o main futlib.o -lOpenCL++When using the OpenCL backend, extra API functions are provided for+directly accessing or providing the OpenCL objects used by Futhark.+Take care when using these functions.  In particular, a Futhark+context can now be provided with the command queue to use::++  struct futhark_context *futhark_context_new_with_command_queue(struct futhark_context_config *cfg, cl_command_queue queue);++As a ``cl_command_queue`` specifies an OpenCL device, this is also how+manual platform and device selection is possible.  A function is also+provided for retrieving the command queue used by some Futhark+context::++  cl_command_queue futhark_context_get_command_queue(struct futhark_context *ctx);++This can be used to connect two separate Futhark contexts that have+been loaded dynamically.++The raw ``cl_mem`` object underlying a Futhark array can be accessed+with the function named ``futhark_values_raw_type``, where ``type``+depends on the array in question.  For example::++  cl_mem futhark_values_raw_i32_1d(struct futhark_context *ctx, struct futhark_i32_1d *arr);++The array will be stored in row-major form in the returned memory+object.  The function performs no copying, so the ``cl_mem`` still+belongs to Futhark, and may be reused for other purposes when the+corresponding array is freed.  A dual function can be used to+construct a Futhark array from a ``cl_mem``::++  struct futhark_i32_1d *futhark_new_raw_i32_1d(struct futhark_context *ctx,+                                                cl_mem data,+                                                int offset,+                                                int dim0);++This function *does* copy the provided memory into fresh internally+allocated memory.  The array is assumed to be stored in row-major form+``offset`` bytes into the memory region.++See also :ref:`futhark-opencl(1)`.++Generating Python+^^^^^^^^^^^^^^^^^++The ``futhark py`` and ``futhark pyopencl`` compilers both support+generating reusable Python code, although the latter of these+generates code of sufficient performance to be worthwhile.  The+following mentions options and parameters only available for+``futhark pyopencl``.  You will need at least PyOpenCL version 2015.2.++We can use ``futhark pyopencl`` to translate the program+``futlib.fut`` into a Python module ``futlib.py`` with the following+command::++  $ futhark pyopencl --library futlib.fut++This will create a file ``futlib.py``, which contains Python code that+defines a class named ``futlib``.  This class defines one method for+each entry point function (see :ref:`entry-points`) in the Futhark+program.  The methods take one parameter for each parameter in the+corresponding entry point, and return a tuple containing a value for+every value returned by the entry point.  For entry points returning a+single (non-tuple) value, just that value is returned (that is,+single-element tuples are not returned).++After the class has been instantiated, these methods can be invoked to+run the corresponding Futhark function.  The constructor for the class+takes various keyword parameters:++  ``interactive=BOOL``++    If ``True`` (the default is ``False``), show a menu of available+    OpenCL platforms and devices, and use the one chosen by the user.++  ``platform_pref=STR``++    Use the first platform that contains the given string.  Similar to+    the ``-p`` option for executables.++  ``device_pref=STR``++    Use the first device that contains the given string.  Similar to+    the ``-d`` option for executables.++Futhark arrays are mapped to either the Numpy ``ndarray`` type or the+`pyopencl.array <https://documen.tician.de/pyopencl/array.html>`_+type.  Scalars are mapped to Numpy scalar types.
+ docs/versus-other-languages.rst view
@@ -0,0 +1,195 @@+.. _versus-other-languages:++Futhark Compared to Other Functional Languages+==============================================++This guide is intended for programmers who are familiar with other functional+languages and want to start working with Futhark.++Futhark is a simple language with a complex compiler.+Functional programming is fundamentally well suited to+data-parallelism, so Futhark's syntax and underlying concepts are taken directly+from established functional languages; mostly from Haskell and the+members of the ML family.  While Futhark does add a few small+conveniences (built-in array types) and one complicated and unusual+feature (in-place updates via uniqueness types, see+:ref:`in-place-updates`), a programmer familiar with a common+functional language should be able to understand the meaning of a+Futhark program, and quickly begin writing their own programs.  To+speed up this process, we describe here some of the various+quirks and unexpected limitations imposed by Futhark. We also+recommended reading some of the `example programs`_ along with this guide.+The guide does *not* cover all Futhark features worth knowing, so do also+skim :ref:`language-reference`.++.. _`example programs`: https://github.com/diku-dk/futhark/tree/master/examples++Basic Syntax+------------++Futhark uses a keyword-based structure, with optional indentation+*solely* for human readability.  This aspect differs from Haskell and F#.++Names are lexically divided into *identifiers* and *symbols*:++* *Identifiers* begin with a letter or underscore and contain letters,+  numbers, underscores, and apostrophes.++* *Symbols* contain the characters found in the default operators+  (``+-*/%=!><|&^``)++All function and variable names must be identifiers, and all infix+operators are symbols.  An identifier can be used as an infix operator+by enclosing it in backticks, as in Haskell.++Identifiers are case-sensitive, and there is no restriction on the+case of the first letter (unlike Haskell and OCaml, but like Standard+ML).++User-defined operators are possible, but the fixity of the operator+depends on its name.  Specifically, the fixity of a user-defined+operator *op* is equal to the fixity of the built-in operator that is+the longest prefix of *op*.  For example, ``<<=`` would have the+same fixity as ``<<``, and ``=<<`` the same as ``=``.  This rule is the+same as the rule found in OCaml and F#.++Top-level functions and values are defined with ``let``, as in OCaml+and F#.++Evaluation+----------++Futhark is a completely pure language, with no cheating through monads+or anything of the sort.++Evaluation is *eager* or *call-by-value*, like most non-Haskell+languages.  However, there is no defined evaluation order.+Furthermore, the Futhark compiler is permitted to turn non-terminating+programs into terminating programs, for example by removing dead code+that might cause an error.  Moreover, there is no way to+handle errors within a Futhark program (no exceptions or similar);+although errors are gracefully reported to whatever invokes the+Futhark program.++The evaluation semantics are entirely sequential, with parallelism+being solely an operational detail.  Hence, race conditions are+impossible. The Futhark compiler does not automatically go+looking for parallelism.  Only certain special constructs and built-in+library functions (in particular ``map``, ``reduce``, ``scan``, and+``filter``) may be executed in parallel.++Currying and partial application work as usual (although functions+are not fully first class; see `Types`_).  Some Futhark language+constructs look like functions, but are not.  This means they cannot+be partially applied.  These include ``unsafe`` and ``assert``.++Lambda terms are written as ``\x -> x + 2``, as in Haskell.++A Futhark program is read top-down, and all functions must be declared+in the order they are used, like Standard ML.  Unlike just+about all functional languages, recursive functions are *not*+supported.  Most of the time, you will use bulk array operations+instead, but there is also a dedicated ``loop`` language construct,+which is essentially syntactic sugar for tail recursive functions.++Types+-----++Futhark supports a range of integer types, floating point types, and+booleans (see :ref:`primitives`).  A numeric literal can be suffixed+with its desired type, such as ``1i8`` for an eight-bit signed+integer.  Un-adorned numerals have their type inferred based on use.+This only works for built-in numeric types.++Arrays are a built-in type.  The type of an array containing elements+of type ``t`` is written ``[]t`` (not ``[t]`` as in Haskell), and we+may optionally annotate it with a size as ``[n]t`` (see `Shape+Declarations`).  Array values are written as ``[1,2,3]``.  Array+indexing is written ``a[i]`` with *no* space allowed between the array+name and the brace.  Indexing of multi-dimensional arrays is written+``a[i,j]``.  Arrays are 0-indexed.++All types can be combined in tuples as usual, as well as in+*structurally typed records*, as in Standard ML.  Non-recursive sum+types are supported, and are also structurally typed.  Abstract types+are possible via the module system; see :ref:`module-system`.++If a variable ``foo`` is a record of type ``{a: i32, b: bool}``, then+we access field ``a`` with dot notation: ``foo.a``.  Tuples are a+special case of records, where all the fields have a 0-indexed numeric+label.  For example, ``(i32, bool)`` is the same as ``{0: i32, 1:+bool}``, and can be indexed as ``foo.1``.++Sum types are defined as constructors separated by a vertical bar+(``|``).  Constructor names always start with a ``#``.  For example,+``#red | #blue i32`` is a sum type with the constructors ``#red`` and+``#blue``, where the latter has an ``i32`` as payload.  The terms+``#red`` and ``#blue 2`` produce values of this type.  Constructor+applications must always be fully saturated.  Due to the structural+typing, type annotations are usually necessary to resolve ambiguities.+For example, the term ``#blue 2`` can produce a value of *any type*+that has an appropriate constructor.++Function types are supported with the usual ``a -> b``, and functions can be+passed as arguments to other functions.  However, there are some+restrictions:++* A function cannot be put in an array (but a record or tuple is+  fine).++* A function cannot be returned from a branch.++* A function cannot be used as a ``loop`` parameter.++Function types interact with type parameters in a subtle way::++  let id 't (x: t) = x++This declaration defines a function ``id`` that has a type parameter+``t``.  Here, ``t`` is an *unlifted* type parameter, which is+guaranteed never to be a function type, and so in the body of the+function we could choose to put parameter values of type ``t`` in an+array.  However, it means that this identity function cannot be called+on a functional value.  Instead, we probably want a *lifted* type+parameter::++  let id '^t (x: t) = x++Such *lifted* type parameters are not restricted from being+instantiated with function types.  On the other hand, in the function+definition they are subject to the same restrictions as functional+types.++Futhark supports Hindley-Milner type inference (with some+restrictions), so we could also just write it as::++  let id x = x++Type abbreviations are possible::++  type foo = (i32, i32)++Type parameters are supported as well::++  type pair 'a 'b = (a, b)++As with everything else, they are structurally typed, so the types+``pair i32 bool`` and ``(i32, bool)`` are entirely interchangeable.+Most unusually, this is also the case for sum types.  The following+two types are entirely interchangeable::++  type maybe 'a = #just a | #nothing++  type option 'a = #nothing | #just a++Only for abstract types, where the definition has been hidden via the+module system, do type names have any significance.++Size parameters can also be passed::++  type vector [n] t = [n]t+  type i32matrix [n][m] = [n] (vector [m] i32)++Note that for an actual array type, the dimensions come *before* the+element type, but with a type abbreviation, a size is just another+parameter.  This easily becomes hard to read if you are not careful.
futhark.cabal view
@@ -1,13 +1,7 @@-cabal-version: 1.12---- This file has been generated from package.yaml by hpack version 0.31.2.------ see: https://github.com/sol/hpack------ hash: f4cfd9b833eff0672980c67287441d068079b0a227279aa3a06a58262a3833c6+cabal-version: 2.4  name:           futhark-version:        0.14.1+version:        0.15.1 synopsis:       An optimising compiler for a functional, array-oriented language. description:    Futhark is a small programming language designed to be compiled to                 efficient parallel code. It is a statically typed, data-parallel,@@ -25,55 +19,16 @@ license-file:   LICENSE build-type:     Simple extra-source-files:-    rts/python/memory.py-    rts/python/opencl.py-    rts/python/panic.py-    rts/python/scalar.py-    rts/python/tuning.py-    rts/python/values.py-    rts/c/cuda.h-    rts/c/free_list.h-    rts/c/lock.h-    rts/c/opencl.h-    rts/c/panic.h-    rts/c/timing.h-    rts/c/tuning.h-    rts/c/values.h-    rts/csharp/exceptions.cs-    rts/csharp/functions.cs-    rts/csharp/memory.cs-    rts/csharp/memory_opencl.cs-    rts/csharp/opencl.cs-    rts/csharp/panic.cs-    rts/csharp/reader.cs-    rts/csharp/scalar.cs-    rts/futhark-doc/style.css-    futlib/array.fut-    futlib/functional.fut-    futlib/math.fut-    futlib/prelude.fut-    futlib/soacs.fut-    futlib/zip.fut+    rts/c/*.h+    rts/csharp/*.cs+    rts/futhark-doc/*.css+    rts/python/*.py+    prelude/*.fut+-- Just enough of the docs to build the manpages.+    docs/**/*.rst     docs/Makefile     docs/conf.py-    docs/index.rst-    docs/man/futhark-autotune.rst-    docs/man/futhark-bench.rst-    docs/man/futhark-c.rst-    docs/man/futhark-csharp.rst-    docs/man/futhark-csopencl.rst-    docs/man/futhark-cuda.rst-    docs/man/futhark-dataset.rst-    docs/man/futhark-doc.rst-    docs/man/futhark-opencl.rst-    docs/man/futhark-pkg.rst-    docs/man/futhark-pyopencl.rst-    docs/man/futhark-python.rst-    docs/man/futhark-repl.rst-    docs/man/futhark-run.rst-    docs/man/futhark-test.rst-    docs/man/futhark.rst-    package.yaml+    docs/requirements.txt  source-repository head   type: git@@ -91,6 +46,7 @@       Futhark.Analysis.Metrics       Futhark.Analysis.PrimExp       Futhark.Analysis.PrimExp.Convert+      Futhark.Analysis.PrimExp.Generalize       Futhark.Analysis.PrimExp.Simplify       Futhark.Analysis.Range       Futhark.Analysis.Rephrase@@ -104,6 +60,7 @@       Futhark.CLI.Autotune       Futhark.CLI.Bench       Futhark.CLI.C+      Futhark.CLI.Check       Futhark.CLI.CSharp       Futhark.CLI.CSOpenCL       Futhark.CLI.CUDA@@ -265,7 +222,7 @@       Language.Futhark       Language.Futhark.Attributes       Language.Futhark.Core-      Language.Futhark.Futlib+      Language.Futhark.Prelude       Language.Futhark.Interpreter       Language.Futhark.Parser       Language.Futhark.Pretty@@ -284,9 +241,14 @@       Language.Futhark.Parser.Parser       Language.Futhark.Parser.Lexer       Paths_futhark+  autogen-modules:+      Paths_futhark   hs-source-dirs:       src   ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists+  build-tool-depends:+      alex:alex+    , happy:happy   build-depends:       aeson >=1.0.0.0     , ansi-terminal >=0.6.3.1@@ -334,9 +296,6 @@     , versions >=3.3.1     , zip-archive >=0.3.1.1     , zlib >=0.6.1.2-  build-tools:-      alex-    , happy   default-language: Haskell2010  executable futhark
− futlib/array.fut
@@ -1,144 +0,0 @@--- | Utility functions for arrays.--import "math"-import "soacs"-import "functional"-open import "zip" -- Rexport.---- | The size of the outer dimension of an array.-let length [n] 't (_: [n]t) = n---- | Is the array empty?-let null [n] 't (_: [n]t) = n == 0---- | The first element of the array.-let head [n] 't (x: [n]t) = x[0]---- | The last element of the array.-let last [n] 't (x: [n]t) = x[n-1]---- | Everything but the first element of the array.-let tail [n] 't (x: [n]t) = x[1:]---- | Everything but the last element of the array.-let init [n] 't (x: [n]t) = x[0:n-1]---- | Take some number of elements from the head of the array.-let take [n] 't (i: i32) (x: [n]t): [i]t = x[0:i]---- | Remove some number of elements from the head of the array.-let drop [n] 't (i: i32) (x: [n]t) = x[i:]---- | Split an array at a given position.-let split 't (n: i32) (xs: []t): ([n]t, []t) =-  (xs[:n], xs[n:])---- | Split an array at two given positions.-let split2 't (i: i32) (j: i32) (xs: []t): ([i]t, []t, []t) =-  (xs[:i], xs[i:j], xs[j:])---- | Return the elements of the array in reverse order.-let reverse [n] 't (x: [n]t): [n]t = x[::-1]---- | Concatenate two arrays.  Warning: never try to perform a reduction--- with this operator; it will not work.-let (++) 't (xs: []t) (ys: []t): *[]t = intrinsics.concat (xs, ys)---- | An old-fashioned way of saying `++`.-let concat 't (xs: []t) (ys: []t): *[]t = xs ++ ys---- | Rotate an array some number of elements to the left.  A negative--- rotation amount is also supported.------ For example, if `b==rotate r a`, then `b[x+r] = a[x]`.-let rotate 't (r: i32) (xs: []t) = intrinsics.rotate (r, xs)---- | Replace an element of the array with a new value.-let update [n] 't (xs: *[n]t) (i: i32) (x: t): *[n]t = xs with [i] = x---- | Construct an array of consecutive integers of the given length,--- starting at 0.-let iota (n: i32): *[n]i32 =-  i32.iota n---- | Construct an array of the given length containing the given--- value.-let replicate 't (n: i32) (x: t): *[n]t =-  i32.replicate n x---- | Copy a value.  The result will not alias anything.-let copy 't (a: t): *t =-  ([a])[0]---- | Combines the outer two dimensions of an array.-let flatten [n][m] 't (xs: [n][m]t): []t =-  intrinsics.flatten xs---- | Combines the outer three dimensions of an array.-let flatten_3d [n][m][l] 't (xs: [n][m][l]t): []t =-  flatten (flatten xs)---- | Combines the outer four dimensions of an array.-let flatten_4d [n][m][l][k] 't (xs: [n][m][l][k]t): []t =-  flatten (flatten_3d xs)---- | Splits the outer dimension of an array in two.-let unflatten 't (n: i32) (m: i32) (xs: []t): [n][m]t =-  intrinsics.unflatten (n, m, xs)---- | Splits the outer dimension of an array in three.-let unflatten_3d 't (n: i32) (m: i32) (l: i32) (xs: []t): [n][m][l]t =-  unflatten n m (unflatten (n*m) l xs)---- | Splits the outer dimension of an array in four.-let unflatten_4d 't (n: i32) (m: i32) (l: i32) (k: i32) (xs: []t): [n][m][l][k]t =-  unflatten n m (unflatten_3d (n*m) l k xs)--let intersperse [n] 't (x: t) (xs: [n]t): *[]t =-  map (\i -> if i % 2 == 1 && i != 2*n then x-             else unsafe xs[i/2])-      (iota (i32.max (2*n-1) 0))--let intercalate [n] [m] 't (x: [m]t) (xs: [n][m]t): []t =-  unsafe flatten (intersperse x xs)--let transpose [n] [m] 't (a: [n][m]t): [m][n]t =-  intrinsics.transpose a--let steps (start: i32) (num_steps: i32) (step: i32): [num_steps]i32 =-  map (start+) (map (step*) (iota num_steps))--let range (start: i32) (end: i32) (step: i32): []i32 =-  let w = (end-start)/step-  in steps start w step---- | True if all of the input elements are true.  Produces true on an--- empty array.-let and: []bool -> bool = all id---- | True if any of the input elements are true.  Produces false on an--- empty array.-let or: []bool -> bool = any id--let pick [n] 't (flags: [n]bool) (xs: [n]t) (ys: [n]t): *[n]t =-  map3 (\flag x y -> if flag then x else y) flags xs ys---- | Perform a *sequential* left-fold of an array.-let foldl 'a 'b (f: a -> b -> a) (acc: a) (bs: []b): a =-  loop acc for b in bs do f acc b---- | Perform a *sequential* right-fold of an array.-let foldr 'a 'b (f: b -> a -> a) (acc: a) (bs: []b): a =-  foldl (flip f) acc (reverse bs)---- | Create a value for each point in a one-dimensional index space.-let tabulate 'a (n: i32) (f: i32 -> a): *[n]a =-  map1 f (iota n)---- | Create a value for each point in a two-dimensional index space.-let tabulate_2d 'a (n: i32) (m: i32) (f: i32 -> i32 -> a): *[n][m]a =-  map1 (f >-> tabulate m) (iota n)---- | Create a value for each point in a three-dimensional index space.-let tabulate_3d 'a (n: i32) (m: i32) (o: i32) (f: i32 -> i32 -> i32 -> a): *[n][m][o]a =-  map1 (f >-> tabulate_2d m o) (iota n)
− futlib/functional.fut
@@ -1,57 +0,0 @@--- | Simple functional combinators.---- | Left-to-right application.  Particularly useful for describing--- computation pipelines:------ ```--- x |> f |> g |> h--- ```-let (|>) '^a '^b (x: a) (f: a -> b): b = f x---- | Right to left application.-let (<|) '^a '^b (f: a -> b) (x: a) = f x---- | Function composition, with values flowing from left to right.-let (>->) '^a '^b '^c (f: a -> b) (g: b -> c) (x: a): c = g (f x)---- | Function composition, with values flowing from right to left.--- This is the same as the `∘` operator known from mathematics.-let (<-<) '^a '^b '^c (g: b -> c) (f: a -> b) (x: a): c = g (f x)---- | Flip the arguments passed to a function.------ ```--- f x y == flip f y x--- ```-let flip '^a '^b '^c (f: a -> b -> c) (b: b) (a: a): c =-  f a b---- | Transform a function taking a pair into a function taking two--- arguments.-let curry '^a '^b '^c (f: (a, b) -> c) (a: a) (b: b): c =-  f (a, b)---- | Transform a function taking two arguments in a function taking a--- pair.-let uncurry '^a '^b '^c (f: a -> b -> c) (a: a, b: b): c =-  f a b---- | The constant function.-let const '^a '^b (x: a) (_: b): a = x---- | The identity function.-let id '^a (x: a) = x---- | Apply a function some number of times.-let iterate 'a (n: i32) (f: a -> a) (x: a) =-  loop x for _i < n do f x---- | Keep applying `f` until `p` returns true for the input value.--- May apply zero times.  *Note*: may not terminate.-let iterate_until 'a (p: a -> bool) (f: a -> a) (x: a) =-  loop x while ! (p x) do f x---- | Keep applying `f` while `p` returns true for the input value.--- May apply zero times.  *Note*: may not terminate.-let iterate_while 'a (p: a -> bool) (f: a -> a) (x: a) =-  loop x while p x do f x
− futlib/math.fut
@@ -1,992 +0,0 @@--- | Basic mathematical modules and functions.--import "soacs"--local let const 'a 'b (x: a) (_: b): a = x---- | Describes types of values that can be created from the primitive--- numeric types (and bool).-module type from_prim = {-  type t--  val i8: i8 -> t-  val i16: i16 -> t-  val i32: i32 -> t-  val i64: i64 -> t--  val u8: u8 -> t-  val u16: u16 -> t-  val u32: u32 -> t-  val u64: u64 -> t--  val f32: f32 -> t-  val f64: f64 -> t--  val bool: bool -> t-}---- | A basic numeric module type that can be implemented for both--- integers and rational numbers.-module type numeric = {-  include from_prim--  val +: t -> t -> t-  val -: t -> t -> t-  val *: t -> t -> t-  val /: t -> t -> t-  val %: t -> t -> t-  val **: t -> t -> t--  val to_i64: t -> i64--  val ==: t -> t -> bool-  val <: t -> t -> bool-  val >: t -> t -> bool-  val <=: t -> t -> bool-  val >=: t -> t -> bool-  val !=: t -> t -> bool--  val negate: t-> t-  val max: t -> t -> t-  val min: t -> t -> t--  val abs: t -> t--  val sgn: t -> t--  -- | The highest representable number.-  val highest: t--  -- | The lowest representable number.-  val lowest: t--  -- | Returns zero on empty input.-  val sum [n]: [n]t -> t--  -- | Returns one on empty input.-  val product [n]: [n]t -> t--  -- | Returns `lowest` on empty input.-  val maximum [n]: [n]t -> t-  -- | Returns `highest` on empty input.-  val minimum [n]: [n]t -> t-}---- | An extension of `numeric`@mtype that provides facilities that are--- only meaningful for integral types.-module type integral = {-  include numeric--  val //: t -> t -> t-  val %%: t -> t -> t--  val &: t -> t -> t-  val |: t -> t -> t-  val ^: t -> t -> t-  val !: t -> t--  val <<: t -> t -> t-  val >>: t -> t -> t-  val >>>: t -> t -> t--  val num_bits: i32-  val get_bit: i32 -> t -> i32-  val set_bit: i32 -> t -> i32 -> t--  -- | Count number of one bits.-  val popc: t -> i32--  -- | Count number of zero bits preceding the most significant set-  -- bit.-  val clz: t -> i32-}---- | An extension of `size`@mtype that further includes facilities for--- constructing arrays where the size is provided as a value of the--- given integral type.-module type size = {-  include integral--  val iota: t -> *[]t-  val replicate 'v: t -> v -> *[]v-}---- | Numbers that model real numbers to some degree.-module type real = {-  include numeric--  val from_fraction: i32 -> i32 -> t-  val to_i32: t -> i32-  val to_i64: t -> i64-  val to_f64: t -> f64--  val sqrt: t -> t-  val exp: t -> t-  val cos: t -> t-  val sin: t -> t-  val tan: t -> t-  val asin: t -> t-  val acos: t -> t-  val atan: t -> t-  val atan2: t -> t -> t-  val gamma: t -> t-  val lgamma: t -> t-  -- | Linear interpolation.  The third argument must be in the range-  -- `[0,1]` or the results are unspecified.-  val lerp: t -> t -> t -> t--  -- | Natural logarithm.-  val log: t -> t-  -- | Base-2 logarithm.-  val log2: t -> t-  -- | Base-10 logarithm.-  val log10: t -> t--  val ceil : t -> t-  val floor : t -> t-  val trunc : t -> t--  -- | Round to the nearest integer, with alfway cases rounded to the-  -- nearest even integer.  Note that this differs from `round()` in-  -- C, but matches more modern languages.-  val round : t -> t--  val isinf: t -> bool-  val isnan: t -> bool--  val inf: t-  val nan: t--  val pi: t-  val e: t-}---- | An extension of `real`@mtype that further gives access to the--- bitwise representation of the underlying number.  It is presumed--- that this will be some form of IEEE float.-module type float = {-  include real--  -- | An unsigned integer type containing the same number of bits as-  -- 't'.-  type int_t--  val from_bits: int_t -> t-  val to_bits: t -> int_t--  val num_bits: i32-  val get_bit: i32 -> t -> i32-  val set_bit: i32 -> t -> i32 -> t-}---- | Boolean numbers.  When converting from a number to `bool`, 0 is--- considered `false` and any other value is `true`.-module bool: from_prim with t = bool = {-  type t = bool--  let i8  = intrinsics.itob_i8_bool-  let i16 = intrinsics.itob_i16_bool-  let i32 = intrinsics.itob_i32_bool-  let i64 = intrinsics.itob_i64_bool--  let u8  (x: u8)  = intrinsics.itob_i8_bool (intrinsics.sign_i8 x)-  let u16 (x: u16) = intrinsics.itob_i16_bool (intrinsics.sign_i16 x)-  let u32 (x: u32) = intrinsics.itob_i32_bool (intrinsics.sign_i32 x)-  let u64 (x: u64) = intrinsics.itob_i64_bool (intrinsics.sign_i64 x)--  let f32 (x: f32) = x != 0f32-  let f64 (x: f64) = x != 0f64--  let bool (x: bool) = x-}--module i8: (size with t = i8) = {-  type t = i8--  let (x: i8) + (y: i8) = intrinsics.add8 (x, y)-  let (x: i8) - (y: i8) = intrinsics.sub8 (x, y)-  let (x: i8) * (y: i8) = intrinsics.mul8 (x, y)-  let (x: i8) / (y: i8) = intrinsics.sdiv8 (x, y)-  let (x: i8) ** (y: i8) = intrinsics.pow8 (x, y)-  let (x: i8) % (y: i8) = intrinsics.smod8 (x, y)-  let (x: i8) // (y: i8) = intrinsics.squot8 (x, y)-  let (x: i8) %% (y: i8) = intrinsics.srem8 (x, y)--  let (x: i8) & (y: i8) = intrinsics.and8 (x, y)-  let (x: i8) | (y: i8) = intrinsics.or8 (x, y)-  let (x: i8) ^ (y: i8) = intrinsics.xor8 (x, y)-  let ! (x: i8) = intrinsics.complement8 x--  let (x: i8) << (y: i8) = intrinsics.shl8 (x, y)-  let (x: i8) >> (y: i8) = intrinsics.ashr8 (x, y)-  let (x: i8) >>> (y: i8) = intrinsics.lshr8 (x, y)--  let i8  (x: i8)  = intrinsics.sext_i8_i8 x-  let i16 (x: i16) = intrinsics.sext_i16_i8 x-  let i32 (x: i32) = intrinsics.sext_i32_i8 x-  let i64 (x: i64) = intrinsics.sext_i64_i8 x--  let u8  (x: u8)  = intrinsics.zext_i8_i8 (intrinsics.sign_i8 x)-  let u16 (x: u16) = intrinsics.zext_i16_i8 (intrinsics.sign_i16 x)-  let u32 (x: u32) = intrinsics.zext_i32_i8 (intrinsics.sign_i32 x)-  let u64 (x: u64) = intrinsics.zext_i64_i8 (intrinsics.sign_i64 x)--  let f32 (x: f32) = intrinsics.fptosi_f32_i8 x-  let f64 (x: f64) = intrinsics.fptosi_f64_i8 x--  let bool = intrinsics.btoi_bool_i8--  let to_i32(x: i8) = intrinsics.sext_i8_i32 x-  let to_i64(x: i8) = intrinsics.sext_i8_i64 x--  let (x: i8) == (y: i8) = intrinsics.eq_i8 (x, y)-  let (x: i8) < (y: i8) = intrinsics.slt8 (x, y)-  let (x: i8) > (y: i8) = intrinsics.slt8 (y, x)-  let (x: i8) <= (y: i8) = intrinsics.sle8 (x, y)-  let (x: i8) >= (y: i8) = intrinsics.sle8 (y, x)-  let (x: i8) != (y: i8) = intrinsics.! (x == y)--  let sgn (x: i8) = intrinsics.ssignum8 x-  let abs (x: i8) = intrinsics.abs8 x--  let negate (x: t) = -x-  let max (x: t) (y: t) = intrinsics.smax8 (x, y)-  let min (x: t) (y: t) = intrinsics.smin8 (x, y)--  let highest = 127i8-  let lowest = highest + 1i8--  let num_bits = 8-  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)-  let set_bit (bit: i32) (x: t) (b: i32) =-    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))-  let popc = intrinsics.popc8-  let clz = intrinsics.clz8--  let iota (n: i8) = 0i8..1i8..<n-  let replicate 'v (n: i8) (x: v) = map (const x) (iota n)--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module i16: (size with t = i16) = {-  type t = i16--  let (x: i16) + (y: i16) = intrinsics.add16 (x, y)-  let (x: i16) - (y: i16) = intrinsics.sub16 (x, y)-  let (x: i16) * (y: i16) = intrinsics.mul16 (x, y)-  let (x: i16) / (y: i16) = intrinsics.sdiv16 (x, y)-  let (x: i16) ** (y: i16) = intrinsics.pow16 (x, y)-  let (x: i16) % (y: i16) = intrinsics.smod16 (x, y)-  let (x: i16) // (y: i16) = intrinsics.squot16 (x, y)-  let (x: i16) %% (y: i16) = intrinsics.srem16 (x, y)--  let (x: i16) & (y: i16) = intrinsics.and16 (x, y)-  let (x: i16) | (y: i16) = intrinsics.or16 (x, y)-  let (x: i16) ^ (y: i16) = intrinsics.xor16 (x, y)-  let ! (x: i16) = intrinsics.complement16 x--  let (x: i16) << (y: i16) = intrinsics.shl16 (x, y)-  let (x: i16) >> (y: i16) = intrinsics.ashr16 (x, y)-  let (x: i16) >>> (y: i16) = intrinsics.lshr16 (x, y)--  let i8  (x: i8)  = intrinsics.sext_i8_i16 x-  let i16 (x: i16) = intrinsics.sext_i16_i16 x-  let i32 (x: i32) = intrinsics.sext_i32_i16 x-  let i64 (x: i64) = intrinsics.sext_i64_i16 x--  let u8  (x: u8)  = intrinsics.zext_i8_i16 (intrinsics.sign_i8 x)-  let u16 (x: u16) = intrinsics.zext_i16_i16 (intrinsics.sign_i16 x)-  let u32 (x: u32) = intrinsics.zext_i32_i16 (intrinsics.sign_i32 x)-  let u64 (x: u64) = intrinsics.zext_i64_i16 (intrinsics.sign_i64 x)--  let f32 (x: f32) = intrinsics.fptosi_f32_i16 x-  let f64 (x: f64) = intrinsics.fptosi_f64_i16 x--  let bool = intrinsics.btoi_bool_i16--  let to_i32(x: i16) = intrinsics.sext_i16_i32 x-  let to_i64(x: i16) = intrinsics.sext_i16_i64 x--  let (x: i16) == (y: i16) = intrinsics.eq_i16 (x, y)-  let (x: i16) < (y: i16) = intrinsics.slt16 (x, y)-  let (x: i16) > (y: i16) = intrinsics.slt16 (y, x)-  let (x: i16) <= (y: i16) = intrinsics.sle16 (x, y)-  let (x: i16) >= (y: i16) = intrinsics.sle16 (y, x)-  let (x: i16) != (y: i16) = intrinsics.! (x == y)--  let sgn (x: i16) = intrinsics.ssignum16 x-  let abs (x: i16) = intrinsics.abs16 x--  let negate (x: t) = -x-  let max (x: t) (y: t) = intrinsics.smax16 (x, y)-  let min (x: t) (y: t) = intrinsics.smin16 (x, y)--  let highest = 32767i16-  let lowest = highest + 1i16--  let num_bits = 16-  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)-  let set_bit (bit: i32) (x: t) (b: i32) =-    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))-  let popc = intrinsics.popc16-  let clz = intrinsics.clz16--  let iota (n: i16) = 0i16..1i16..<n-  let replicate 'v (n: i16) (x: v) = map (const x) (iota n)--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module i32: (size with t = i32) = {-  type t = i32--  let sign (x: u32) = intrinsics.sign_i32 x-  let unsign (x: i32) = intrinsics.unsign_i32 x--  let (x: i32) + (y: i32) = intrinsics.add32 (x, y)-  let (x: i32) - (y: i32) = intrinsics.sub32 (x, y)-  let (x: i32) * (y: i32) = intrinsics.mul32 (x, y)-  let (x: i32) / (y: i32) = intrinsics.sdiv32 (x, y)-  let (x: i32) ** (y: i32) = intrinsics.pow32 (x, y)-  let (x: i32) % (y: i32) = intrinsics.smod32 (x, y)-  let (x: i32) // (y: i32) = intrinsics.squot32 (x, y)-  let (x: i32) %% (y: i32) = intrinsics.srem32 (x, y)--  let (x: i32) & (y: i32) = intrinsics.and32 (x, y)-  let (x: i32) | (y: i32) = intrinsics.or32 (x, y)-  let (x: i32) ^ (y: i32) = intrinsics.xor32 (x, y)-  let ! (x: i32) = intrinsics.complement32 x--  let (x: i32) << (y: i32) = intrinsics.shl32 (x, y)-  let (x: i32) >> (y: i32) = intrinsics.ashr32 (x, y)-  let (x: i32) >>> (y: i32) = intrinsics.lshr32 (x, y)--  let i8  (x: i8)  = intrinsics.sext_i8_i32 x-  let i16 (x: i16) = intrinsics.sext_i16_i32 x-  let i32 (x: i32) = intrinsics.sext_i32_i32 x-  let i64 (x: i64) = intrinsics.sext_i64_i32 x--  let u8  (x: u8)  = intrinsics.zext_i8_i32 (intrinsics.sign_i8 x)-  let u16 (x: u16) = intrinsics.zext_i16_i32 (intrinsics.sign_i16 x)-  let u32 (x: u32) = intrinsics.zext_i32_i32 (intrinsics.sign_i32 x)-  let u64 (x: u64) = intrinsics.zext_i64_i32 (intrinsics.sign_i64 x)--  let f32 (x: f32) = intrinsics.fptosi_f32_i32 x-  let f64 (x: f64) = intrinsics.fptosi_f64_i32 x--  let bool = intrinsics.btoi_bool_i32--  let to_i32(x: i32) = intrinsics.sext_i32_i32 x-  let to_i64(x: i32) = intrinsics.sext_i32_i64 x--  let (x: i32) == (y: i32) = intrinsics.eq_i32 (x, y)-  let (x: i32) < (y: i32) = intrinsics.slt32 (x, y)-  let (x: i32) > (y: i32) = intrinsics.slt32 (y, x)-  let (x: i32) <= (y: i32) = intrinsics.sle32 (x, y)-  let (x: i32) >= (y: i32) = intrinsics.sle32 (y, x)-  let (x: i32) != (y: i32) = intrinsics.! (x == y)--  let sgn (x: i32) = intrinsics.ssignum32 x-  let abs (x: i32) = intrinsics.abs32 x--  let negate (x: t) = -x-  let max (x: t) (y: t) = intrinsics.smax32 (x, y)-  let min (x: t) (y: t) = intrinsics.smin32 (x, y)--  let highest = 2147483647-  let lowest = highest + 1--  let num_bits = 32-  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)-  let set_bit (bit: i32) (x: t) (b: i32) =-    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))-  let popc = intrinsics.popc32-  let clz = intrinsics.clz32--  let iota (n: i32) = 0..1..<n-  let replicate 'v (n: i32) (x: v) = map (const x) (iota n)--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module i64: (size with t = i64) = {-  type t = i64--  let sign (x: u64) = intrinsics.sign_i64 x-  let unsign (x: i64) = intrinsics.unsign_i64 x--  let (x: i64) + (y: i64) = intrinsics.add64 (x, y)-  let (x: i64) - (y: i64) = intrinsics.sub64 (x, y)-  let (x: i64) * (y: i64) = intrinsics.mul64 (x, y)-  let (x: i64) / (y: i64) = intrinsics.sdiv64 (x, y)-  let (x: i64) ** (y: i64) = intrinsics.pow64 (x, y)-  let (x: i64) % (y: i64) = intrinsics.smod64 (x, y)-  let (x: i64) // (y: i64) = intrinsics.squot64 (x, y)-  let (x: i64) %% (y: i64) = intrinsics.srem64 (x, y)--  let (x: i64) & (y: i64) = intrinsics.and64 (x, y)-  let (x: i64) | (y: i64) = intrinsics.or64 (x, y)-  let (x: i64) ^ (y: i64) = intrinsics.xor64 (x, y)-  let ! (x: i64) = intrinsics.complement64 x--  let (x: i64) << (y: i64) = intrinsics.shl64 (x, y)-  let (x: i64) >> (y: i64) = intrinsics.ashr64 (x, y)-  let (x: i64) >>> (y: i64) = intrinsics.lshr64 (x, y)--  let i8  (x: i8)  = intrinsics.sext_i8_i64 x-  let i16 (x: i16) = intrinsics.sext_i16_i64 x-  let i32 (x: i32) = intrinsics.sext_i32_i64 x-  let i64 (x: i64) = intrinsics.sext_i64_i64 x--  let u8  (x: u8)  = intrinsics.zext_i8_i64 (intrinsics.sign_i8 x)-  let u16 (x: u16) = intrinsics.zext_i16_i64 (intrinsics.sign_i16 x)-  let u32 (x: u32) = intrinsics.zext_i32_i64 (intrinsics.sign_i32 x)-  let u64 (x: u64) = intrinsics.zext_i64_i64 (intrinsics.sign_i64 x)--  let f32 (x: f32) = intrinsics.fptosi_f32_i64 x-  let f64 (x: f64) = intrinsics.fptosi_f64_i64 x--  let bool = intrinsics.btoi_bool_i64--  let to_i32(x: i64) = intrinsics.sext_i64_i32 x-  let to_i64(x: i64) = intrinsics.sext_i64_i64 x--  let (x: i64) == (y: i64) = intrinsics.eq_i64 (x, y)-  let (x: i64) < (y: i64) = intrinsics.slt64 (x, y)-  let (x: i64) > (y: i64) = intrinsics.slt64 (y, x)-  let (x: i64) <= (y: i64) = intrinsics.sle64 (x, y)-  let (x: i64) >= (y: i64) = intrinsics.sle64 (y, x)-  let (x: i64) != (y: i64) = intrinsics.! (x == y)--  let sgn (x: i64) = intrinsics.ssignum64 x-  let abs (x: i64) = intrinsics.abs64 x--  let negate (x: t) = -x-  let max (x: t) (y: t) = intrinsics.smax64 (x, y)-  let min (x: t) (y: t) = intrinsics.smin64 (x, y)--  let highest = 9223372036854775807i64-  let lowest = highest + 1i64--  let num_bits = 64-  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)-  let set_bit (bit: i32) (x: t) (b: i32) =-    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | intrinsics.zext_i32_i64 (b intrinsics.<< bit))-  let popc = intrinsics.popc64-  let clz = intrinsics.clz64--  let iota (n: i64) = 0i64..1i64..<n-  let replicate 'v (n: i64) (x: v) = map (const x) (iota n)--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module u8: (size with t = u8) = {-  type t = u8--  let sign (x: u8) = intrinsics.sign_i8 x-  let unsign (x: i8) = intrinsics.unsign_i8 x--  let (x: u8) + (y: u8) = unsign (intrinsics.add8 (sign x, sign y))-  let (x: u8) - (y: u8) = unsign (intrinsics.sub8 (sign x, sign y))-  let (x: u8) * (y: u8) = unsign (intrinsics.mul8 (sign x, sign y))-  let (x: u8) / (y: u8) = unsign (intrinsics.udiv8 (sign x, sign y))-  let (x: u8) ** (y: u8) = unsign (intrinsics.pow8 (sign x, sign y))-  let (x: u8) % (y: u8) = unsign (intrinsics.umod8 (sign x, sign y))-  let (x: u8) // (y: u8) = unsign (intrinsics.udiv8 (sign x, sign y))-  let (x: u8) %% (y: u8) = unsign (intrinsics.umod8 (sign x, sign y))--  let (x: u8) & (y: u8) = unsign (intrinsics.and8 (sign x, sign y))-  let (x: u8) | (y: u8) = unsign (intrinsics.or8 (sign x, sign y))-  let (x: u8) ^ (y: u8) = unsign (intrinsics.xor8 (sign x, sign y))-  let ! (x: u8) = unsign (intrinsics.complement8 (sign x))--  let (x: u8) << (y: u8) = unsign (intrinsics.shl8 (sign x, sign y))-  let (x: u8) >> (y: u8) = unsign (intrinsics.ashr8 (sign x, sign y))-  let (x: u8) >>> (y: u8) = unsign (intrinsics.lshr8 (sign x, sign y))--  let u8  (x: u8)  = unsign (i8.u8 x)-  let u16 (x: u16) = unsign (i8.u16 x)-  let u32 (x: u32) = unsign (i8.u32 x)-  let u64 (x: u64) = unsign (i8.u64 x)--  let i8  (x: i8)  = unsign (intrinsics.zext_i8_i8 x)-  let i16 (x: i16) = unsign (intrinsics.zext_i16_i8 x)-  let i32 (x: i32) = unsign (intrinsics.zext_i32_i8 x)-  let i64 (x: i64) = unsign (intrinsics.zext_i64_i8 x)--  let f32 (x: f32) = unsign (intrinsics.fptoui_f32_i8 x)-  let f64 (x: f64) = unsign (intrinsics.fptoui_f64_i8 x)--  let bool x = unsign (intrinsics.btoi_bool_i8 x)--  let to_i32(x: u8) = intrinsics.zext_i8_i32 (sign x)-  let to_i64(x: u8) = intrinsics.zext_i8_i64 (sign x)--  let (x: u8) == (y: u8) = intrinsics.eq_i8 (sign x, sign y)-  let (x: u8) < (y: u8) = intrinsics.ult8 (sign x, sign y)-  let (x: u8) > (y: u8) = intrinsics.ult8 (sign y, sign x)-  let (x: u8) <= (y: u8) = intrinsics.ule8 (sign x, sign y)-  let (x: u8) >= (y: u8) = intrinsics.ule8 (sign y, sign x)-  let (x: u8) != (y: u8) = intrinsics.! (x == y)--  let sgn (x: u8) = unsign (intrinsics.usignum8 (sign x))-  let abs (x: u8) = x--  let negate (x: t) = -x-  let max (x: t) (y: t) = unsign (intrinsics.umax8 (sign x, sign y))-  let min (x: t) (y: t) = unsign (intrinsics.umin8 (sign x, sign y))--  let highest = 255u8-  let lowest = 0u8--  let num_bits = 8-  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)-  let set_bit (bit: i32) (x: t) (b: i32) =-    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))-  let popc x = intrinsics.popc8 (sign x)-  let clz x = intrinsics.clz8 (sign x)--  let iota (n: u8) = 0u8..1u8..<n-  let replicate 'v (n: u8) (x: v) = map (const x) (iota n)--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module u16: (size with t = u16) = {-  type t = u16--  let sign (x: u16) = intrinsics.sign_i16 x-  let unsign (x: i16) = intrinsics.unsign_i16 x--  let (x: u16) + (y: u16) = unsign (intrinsics.add16 (sign x, sign y))-  let (x: u16) - (y: u16) = unsign (intrinsics.sub16 (sign x, sign y))-  let (x: u16) * (y: u16) = unsign (intrinsics.mul16 (sign x, sign y))-  let (x: u16) / (y: u16) = unsign (intrinsics.udiv16 (sign x, sign y))-  let (x: u16) ** (y: u16) = unsign (intrinsics.pow16 (sign x, sign y))-  let (x: u16) % (y: u16) = unsign (intrinsics.umod16 (sign x, sign y))-  let (x: u16) // (y: u16) = unsign (intrinsics.udiv16 (sign x, sign y))-  let (x: u16) %% (y: u16) = unsign (intrinsics.umod16 (sign x, sign y))--  let (x: u16) & (y: u16) = unsign (intrinsics.and16 (sign x, sign y))-  let (x: u16) | (y: u16) = unsign (intrinsics.or16 (sign x, sign y))-  let (x: u16) ^ (y: u16) = unsign (intrinsics.xor16 (sign x, sign y))-  let ! (x: u16) = unsign (intrinsics.complement16 (sign x))--  let (x: u16) << (y: u16) = unsign (intrinsics.shl16 (sign x, sign y))-  let (x: u16) >> (y: u16) = unsign (intrinsics.ashr16 (sign x, sign y))-  let (x: u16) >>> (y: u16) = unsign (intrinsics.lshr16 (sign x, sign y))--  let u8  (x: u8)  = unsign (i16.u8 x)-  let u16 (x: u16) = unsign (i16.u16 x)-  let u32 (x: u32) = unsign (i16.u32 x)-  let u64 (x: u64) = unsign (i16.u64 x)--  let i8  (x: i8)  = unsign (intrinsics.zext_i8_i16 x)-  let i16 (x: i16) = unsign (intrinsics.zext_i16_i16 x)-  let i32 (x: i32) = unsign (intrinsics.zext_i32_i16 x)-  let i64 (x: i64) = unsign (intrinsics.zext_i64_i16 x)--  let f32 (x: f32) = unsign (intrinsics.fptoui_f32_i16 x)-  let f64 (x: f64) = unsign (intrinsics.fptoui_f64_i16 x)--  let bool x = unsign (intrinsics.btoi_bool_i16 x)--  let to_i32(x: u16) = intrinsics.zext_i16_i32 (sign x)-  let to_i64(x: u16) = intrinsics.zext_i16_i64 (sign x)--  let (x: u16) == (y: u16) = intrinsics.eq_i16 (sign x, sign y)-  let (x: u16) < (y: u16) = intrinsics.ult16 (sign x, sign y)-  let (x: u16) > (y: u16) = intrinsics.ult16 (sign y, sign x)-  let (x: u16) <= (y: u16) = intrinsics.ule16 (sign x, sign y)-  let (x: u16) >= (y: u16) = intrinsics.ule16 (sign y, sign x)-  let (x: u16) != (y: u16) = intrinsics.! (x == y)--  let sgn (x: u16) = unsign (intrinsics.usignum16 (sign x))-  let abs (x: u16) = x--  let negate (x: t) = -x-  let max (x: t) (y: t) = unsign (intrinsics.umax16 (sign x, sign y))-  let min (x: t) (y: t) = unsign (intrinsics.umin16 (sign x, sign y))--  let highest = 65535u16-  let lowest = 0u16--  let num_bits = 16-  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)-  let set_bit (bit: i32) (x: t) (b: i32) =-    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))-  let popc x = intrinsics.popc16 (sign x)-  let clz x = intrinsics.clz16 (sign x)--  let iota (n: u16) = 0u16..1u16..<n-  let replicate 'v (n: u16) (x: v) = map (const x) (iota n)--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module u32: (size with t = u32) = {-  type t = u32--  let sign (x: u32) = intrinsics.sign_i32 x-  let unsign (x: i32) = intrinsics.unsign_i32 x--  let (x: u32) + (y: u32) = unsign (intrinsics.add32 (sign x, sign y))-  let (x: u32) - (y: u32) = unsign (intrinsics.sub32 (sign x, sign y))-  let (x: u32) * (y: u32) = unsign (intrinsics.mul32 (sign x, sign y))-  let (x: u32) / (y: u32) = unsign (intrinsics.udiv32 (sign x, sign y))-  let (x: u32) ** (y: u32) = unsign (intrinsics.pow32 (sign x, sign y))-  let (x: u32) % (y: u32) = unsign (intrinsics.umod32 (sign x, sign y))-  let (x: u32) // (y: u32) = unsign (intrinsics.udiv32 (sign x, sign y))-  let (x: u32) %% (y: u32) = unsign (intrinsics.umod32 (sign x, sign y))--  let (x: u32) & (y: u32) = unsign (intrinsics.and32 (sign x, sign y))-  let (x: u32) | (y: u32) = unsign (intrinsics.or32 (sign x, sign y))-  let (x: u32) ^ (y: u32) = unsign (intrinsics.xor32 (sign x, sign y))-  let ! (x: u32) = unsign (intrinsics.complement32 (sign x))--  let (x: u32) << (y: u32) = unsign (intrinsics.shl32 (sign x, sign y))-  let (x: u32) >> (y: u32) = unsign (intrinsics.ashr32 (sign x, sign y))-  let (x: u32) >>> (y: u32) = unsign (intrinsics.lshr32 (sign x, sign y))--  let u8  (x: u8)  = unsign (i32.u8 x)-  let u16 (x: u16) = unsign (i32.u16 x)-  let u32 (x: u32) = unsign (i32.u32 x)-  let u64 (x: u64) = unsign (i32.u64 x)--  let i8  (x: i8)  = unsign (intrinsics.zext_i8_i32 x)-  let i16 (x: i16) = unsign (intrinsics.zext_i16_i32 x)-  let i32 (x: i32) = unsign (intrinsics.zext_i32_i32 x)-  let i64 (x: i64) = unsign (intrinsics.zext_i64_i32 x)--  let f32 (x: f32) = unsign (intrinsics.fptoui_f32_i32 x)-  let f64 (x: f64) = unsign (intrinsics.fptoui_f64_i32 x)--  let bool x = unsign (intrinsics.btoi_bool_i32 x)--  let to_i32(x: u32) = intrinsics.zext_i32_i32 (sign x)-  let to_i64(x: u32) = intrinsics.zext_i32_i64 (sign x)--  let (x: u32) == (y: u32) = intrinsics.eq_i32 (sign x, sign y)-  let (x: u32) < (y: u32) = intrinsics.ult32 (sign x, sign y)-  let (x: u32) > (y: u32) = intrinsics.ult32 (sign y, sign x)-  let (x: u32) <= (y: u32) = intrinsics.ule32 (sign x, sign y)-  let (x: u32) >= (y: u32) = intrinsics.ule32 (sign y, sign x)-  let (x: u32) != (y: u32) = intrinsics.! (x == y)--  let sgn (x: u32) = unsign (intrinsics.usignum32 (sign x))-  let abs (x: u32) = x--  let highest = 4294967295u32-  let lowest = highest + 1u32--  let negate (x: t) = -x-  let max (x: t) (y: t) = unsign (intrinsics.umax32 (sign x, sign y))-  let min (x: t) (y: t) = unsign (intrinsics.umin32 (sign x, sign y))--  let num_bits = 32-  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)-  let set_bit (bit: i32) (x: t) (b: i32) =-    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))-  let popc x = intrinsics.popc32 (sign x)-  let clz x = intrinsics.clz32 (sign x)--  let iota (n: u32) = 0u32..1u32..<n-  let replicate 'v (n: u32) (x: v) = map (const x) (iota n)--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module u64: (size with t = u64) = {-  type t = u64--  let sign (x: u64) = intrinsics.sign_i64 x-  let unsign (x: i64) = intrinsics.unsign_i64 x--  let (x: u64) + (y: u64) = unsign (intrinsics.add64 (sign x, sign y))-  let (x: u64) - (y: u64) = unsign (intrinsics.sub64 (sign x, sign y))-  let (x: u64) * (y: u64) = unsign (intrinsics.mul64 (sign x, sign y))-  let (x: u64) / (y: u64) = unsign (intrinsics.udiv64 (sign x, sign y))-  let (x: u64) ** (y: u64) = unsign (intrinsics.pow64 (sign x, sign y))-  let (x: u64) % (y: u64) = unsign (intrinsics.umod64 (sign x, sign y))-  let (x: u64) // (y: u64) = unsign (intrinsics.udiv64 (sign x, sign y))-  let (x: u64) %% (y: u64) = unsign (intrinsics.umod64 (sign x, sign y))--  let (x: u64) & (y: u64) = unsign (intrinsics.and64 (sign x, sign y))-  let (x: u64) | (y: u64) = unsign (intrinsics.or64 (sign x, sign y))-  let (x: u64) ^ (y: u64) = unsign (intrinsics.xor64 (sign x, sign y))-  let ! (x: u64) = unsign (intrinsics.complement64 (sign x))--  let (x: u64) << (y: u64) = unsign (intrinsics.shl64 (sign x, sign y))-  let (x: u64) >> (y: u64) = unsign (intrinsics.ashr64 (sign x, sign y))-  let (x: u64) >>> (y: u64) = unsign (intrinsics.lshr64 (sign x, sign y))--  let u8  (x: u8)  = unsign (i64.u8 x)-  let u16 (x: u16) = unsign (i64.u16 x)-  let u32 (x: u32) = unsign (i64.u32 x)-  let u64 (x: u64) = unsign (i64.u64 x)--  let i8 (x: i8)   = unsign (intrinsics.zext_i8_i64 x)-  let i16 (x: i16) = unsign (intrinsics.zext_i16_i64 x)-  let i32 (x: i32) = unsign (intrinsics.zext_i32_i64 x)-  let i64 (x: i64) = unsign (intrinsics.zext_i64_i64 x)--  let f32 (x: f32) = unsign (intrinsics.fptoui_f32_i64 x)-  let f64 (x: f64) = unsign (intrinsics.fptoui_f64_i64 x)--  let bool x = unsign (intrinsics.btoi_bool_i64 x)--  let to_i32(x: u64) = intrinsics.zext_i64_i32 (sign x)-  let to_i64(x: u64) = intrinsics.zext_i64_i64 (sign x)--  let (x: u64) == (y: u64) = intrinsics.eq_i64 (sign x, sign y)-  let (x: u64) < (y: u64) = intrinsics.ult64 (sign x, sign y)-  let (x: u64) > (y: u64) = intrinsics.ult64 (sign y, sign x)-  let (x: u64) <= (y: u64) = intrinsics.ule64 (sign x, sign y)-  let (x: u64) >= (y: u64) = intrinsics.ule64 (sign y, sign x)-  let (x: u64) != (y: u64) = intrinsics.! (x == y)--  let sgn (x: u64) = unsign (intrinsics.usignum64 (sign x))-  let abs (x: u64) = x--  let negate (x: t) = -x-  let max (x: t) (y: t) = unsign (intrinsics.umax64 (sign x, sign y))-  let min (x: t) (y: t) = unsign (intrinsics.umin64 (sign x, sign y))--  let highest = 18446744073709551615u64-  let lowest = highest + 1u64--  let num_bits = 64-  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)-  let set_bit (bit: i32) (x: t) (b: i32) =-    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))-  let popc x = intrinsics.popc64 (sign x)-  let clz x = intrinsics.clz64 (sign x)--  let iota (n: u64) = 0u64..1u64..<n-  let replicate 'v (n: u64) (x: v) = map (const x) (iota n)--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module f64: (float with t = f64 with int_t = u64) = {-  type t = f64-  type int_t = u64--  module i64m = i64-  module u64m = u64--  let (x: f64) + (y: f64) = intrinsics.fadd64 (x, y)-  let (x: f64) - (y: f64) = intrinsics.fsub64 (x, y)-  let (x: f64) * (y: f64) = intrinsics.fmul64 (x, y)-  let (x: f64) / (y: f64) = intrinsics.fdiv64 (x, y)-  let (x: f64) % (y: f64) = intrinsics.fmod64 (x, y)-  let (x: f64) ** (y: f64) = intrinsics.fpow64 (x, y)--  let u8  (x: u8)  = intrinsics.uitofp_i8_f64  (i8.u8 x)-  let u16 (x: u16) = intrinsics.uitofp_i16_f64 (i16.u16 x)-  let u32 (x: u32) = intrinsics.uitofp_i32_f64 (i32.u32 x)-  let u64 (x: u64) = intrinsics.uitofp_i64_f64 (i64.u64 x)--  let i8 (x: i8) = intrinsics.sitofp_i8_f64 x-  let i16 (x: i16) = intrinsics.sitofp_i16_f64 x-  let i32 (x: i32) = intrinsics.sitofp_i32_f64 x-  let i64 (x: i64) = intrinsics.sitofp_i64_f64 x--  let f32 (x: f32) = intrinsics.fpconv_f32_f64 x-  let f64 (x: f64) = intrinsics.fpconv_f64_f64 x--  let bool (x: bool) = if x then 1f64 else 0f64--  let from_fraction (x: i32) (y: i32) = i32 x / i32 y-  let to_i32 (x: f64) = intrinsics.fptosi_f64_i32 x-  let to_i64 (x: f64) = intrinsics.fptosi_f64_i64 x-  let to_f64 (x: f64) = x--  let (x: f64) == (y: f64) = intrinsics.eq_f64 (x, y)-  let (x: f64) < (y: f64) = intrinsics.lt64 (x, y)-  let (x: f64) > (y: f64) = intrinsics.lt64 (y, x)-  let (x: f64) <= (y: f64) = intrinsics.le64 (x, y)-  let (x: f64) >= (y: f64) = intrinsics.le64 (y, x)-  let (x: f64) != (y: f64) = intrinsics.! (x == y)--  let negate (x: t) = -x-  let max (x: t) (y: t) = intrinsics.fmax64 (x, y)-  let min (x: t) (y: t) = intrinsics.fmin64 (x, y)--  let sgn (x: f64) = if      x < 0f64  then -1f64-                     else if x == 0f64 then  0f64-                     else                    1f64-  let abs (x: f64) = intrinsics.fabs64 x--  let sqrt (x: f64) = intrinsics.sqrt64 x--  let log (x: f64) = intrinsics.log64 x-  let log2 (x: f64) = intrinsics.log2_64 x-  let log10 (x: f64) = intrinsics.log10_64 x-  let exp (x: f64) = intrinsics.exp64 x-  let cos (x: f64) = intrinsics.cos64 x-  let sin (x: f64) = intrinsics.sin64 x-  let tan (x: f64) = intrinsics.tan64 x-  let acos (x: f64) = intrinsics.acos64 x-  let asin (x: f64) = intrinsics.asin64 x-  let atan (x: f64) = intrinsics.atan64 x-  let atan2 (x: f64) (y: f64) = intrinsics.atan2_64 (x, y)-  let gamma = intrinsics.gamma64-  let lgamma = intrinsics.lgamma64-  let lerp v0 v1 t = intrinsics.lerp64 (v0, v1, t)--  let ceil = intrinsics.ceil64-  let floor = intrinsics.floor64-  let trunc (x: f64) : f64 = i64 (i64m.f64 x)--  let round = intrinsics.round64--  let to_bits (x: f64): u64 = u64m.i64 (intrinsics.to_bits64 x)-  let from_bits (x: u64): f64 = intrinsics.from_bits64 (intrinsics.sign_i64 x)--  let num_bits = 64-  let get_bit (bit: i32) (x: t) = u64m.get_bit bit (to_bits x)-  let set_bit (bit: i32) (x: t) (b: i32) = from_bits (u64m.set_bit bit (to_bits x) b)--  let isinf (x: f64) = intrinsics.isinf64 x-  let isnan (x: f64) = intrinsics.isnan64 x--  let inf = 1f64 / 0f64-  let nan = 0f64 / 0f64--  let highest = inf-  let lowest = -inf--  let pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062f64-  let e = 2.718281828459045235360287471352662497757247093699959574966967627724076630353f64--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}--module f32: (float with t = f32 with int_t = u32) = {-  type t = f32-  type int_t = u32--  module i32m = i32-  module u32m = u32-  module f64m = f64--  let (x: f32) + (y: f32) = intrinsics.fadd32 (x, y)-  let (x: f32) - (y: f32) = intrinsics.fsub32 (x, y)-  let (x: f32) * (y: f32) = intrinsics.fmul32 (x, y)-  let (x: f32) / (y: f32) = intrinsics.fdiv32 (x, y)-  let (x: f32) % (y: f32) = intrinsics.fmod32 (x, y)-  let (x: f32) ** (y: f32) = intrinsics.fpow32 (x, y)--  let u8  (x: u8)  = intrinsics.uitofp_i8_f32  (i8.u8 x)-  let u16 (x: u16) = intrinsics.uitofp_i16_f32 (i16.u16 x)-  let u32 (x: u32) = intrinsics.uitofp_i32_f32 (i32.u32 x)-  let u64 (x: u64) = intrinsics.uitofp_i64_f32 (i64.u64 x)--  let i8 (x: i8) = intrinsics.sitofp_i8_f32 x-  let i16 (x: i16) = intrinsics.sitofp_i16_f32 x-  let i32 (x: i32) = intrinsics.sitofp_i32_f32 x-  let i64 (x: i64) = intrinsics.sitofp_i64_f32 x--  let f32 (x: f32) = intrinsics.fpconv_f32_f32 x-  let f64 (x: f64) = intrinsics.fpconv_f64_f32 x--  let bool (x: bool) = if x then 1f32 else 0f32--  let from_fraction (x: i32) (y: i32) = i32 x / i32 y-  let to_i32 (x: f32) = intrinsics.fptosi_f32_i32 x-  let to_i64 (x: f32) = intrinsics.fptosi_f32_i64 x-  let to_f64 (x: f32) = intrinsics.fpconv_f32_f64 x--  let (x: f32) == (y: f32) = intrinsics.eq_f32 (x, y)-  let (x: f32) < (y: f32) = intrinsics.lt32 (x, y)-  let (x: f32) > (y: f32) = intrinsics.lt32 (y, x)-  let (x: f32) <= (y: f32) = intrinsics.le32 (x, y)-  let (x: f32) >= (y: f32) = intrinsics.le32 (y, x)-  let (x: f32) != (y: f32) = intrinsics.! (x == y)--  let negate (x: t) = -x-  let max (x: t) (y: t) = intrinsics.fmax32 (x, y)-  let min (x: t) (y: t) = intrinsics.fmin32 (x, y)--  let sgn (x: f32) = if      x < 0f32  then -1f32-                     else if x == 0f32 then  0f32-                     else                    1f32-  let abs (x: f32) = intrinsics.fabs32 x--  let sqrt (x: f32) = intrinsics.sqrt32 x--  let log (x: f32) = intrinsics.log32 x-  let log2 (x: f32) = intrinsics.log2_32 x-  let log10 (x: f32) = intrinsics.log10_32 x-  let exp (x: f32) = intrinsics.exp32 x-  let cos (x: f32) = intrinsics.cos32 x-  let sin (x: f32) = intrinsics.sin32 x-  let tan (x: f32) = intrinsics.tan32 x-  let acos (x: f32) = intrinsics.acos32 x-  let asin (x: f32) = intrinsics.asin32 x-  let atan (x: f32) = intrinsics.atan32 x-  let atan2 (x: f32) (y: f32) = intrinsics.atan2_32 (x, y)-  let gamma = intrinsics.gamma32-  let lgamma = intrinsics.lgamma32-  let lerp v0 v1 t = intrinsics.lerp32 (v0, v1, t)--  let ceil = intrinsics.ceil32-  let floor = intrinsics.floor32-  let trunc (x: f32) : f32 = i32 (i32m.f32 x)--  let round = intrinsics.round32--  let to_bits (x: f32): u32 = u32m.i32 (intrinsics.to_bits32 x)-  let from_bits (x: u32): f32 = intrinsics.from_bits32 (intrinsics.sign_i32 x)--  let num_bits = 32-  let get_bit (bit: i32) (x: t) = u32m.get_bit bit (to_bits x)-  let set_bit (bit: i32) (x: t) (b: i32) = from_bits (u32m.set_bit bit (to_bits x) b)--  let isinf (x: f32) = intrinsics.isinf32 x-  let isnan (x: f32) = intrinsics.isnan32 x--  let inf = 1f32 / 0f32-  let nan = 0f32 / 0f32--  let highest = inf-  let lowest = -inf--  let pi = f64 f64m.pi-  let e = f64 f64m.e--  let sum = reduce (+) (i32 0)-  let product = reduce (*) (i32 1)-  let maximum = reduce max lowest-  let minimum = reduce min highest-}
− futlib/prelude.fut
@@ -1,34 +0,0 @@--- | The default prelude that is implicitly available in all Futhark--- files.--open import "soacs"-open import "array"-open import "math"-open import "functional"---- | Create single-precision float from integer.-let r32 (x: i32): f32 = f32.i32 x--- | Create integer from single-precision float.-let t32 (x: f32): i32 = i32.f32 x---- | Create double-precision float from integer.-let r64 (x: i32): f64 = f64.i32 x--- | Create integer from double-precision float.-let t64 (x: f64): i32 = i32.f64 x---- | Semantically just identity, but serves as an optimisation--- inhibitor.  The compiler will treat this function as a black box.--- You can use this to work around optimisation deficiencies (or--- bugs), although it should hopefully rarely be necessary.-let opaque 't (x: t): t =-  intrinsics.opaque x---- | Semantically just identity, but when run in the interpreter, the--- argument value will be printed.-let trace 't (x: t): t =-  intrinsics.trace x---- | Semantically just identity, but acts as a break point in--- `futhark repl`.-let break 't (x: t): t =-  intrinsics.break x
− futlib/soacs.fut
@@ -1,251 +0,0 @@--- | Various Second-Order Array Combinators that are operationally--- parallel in a way that can be exploited by the compiler.------ The functions here are all recognised specially by the compiler (or--- built on those that are).  The asymptotic [work and--- span](https://en.wikipedia.org/wiki/Analysis_of_parallel_algorithms)--- is provided for each function, but note that this easily hides very--- substantial constant factors.  For example, `scan`@term is *much*--- slower than `reduce`@term, although they have the same asymptotic--- complexity.------ *Reminder on terminology*: A function `op` is said to be--- *associative* if------     (x `op` y) `op` z == x `op` (y `op` z)------ for all `x`, `y`, `z`.  Similarly, it is *commutative* if------     x `op` y == y `op` x------ The value `o` is a *neutral element* if------     x `op` o == o `op` x == x------ for any `x`.--import "zip"---- | Apply the given function to each element of an array.------ **Work:** *O(n)*------ **Span:** *O(1)*-let map 'a [n] 'x (f: a -> x) (as: [n]a): *[n]x =-  intrinsics.map (f, as)---- | Apply the given function to each element of a single array.------ **Work:** *O(n)*------ **Span:** *O(1)*-let map1 'a [n] 'x (f: a -> x) (as: [n]a): *[n]x =-  map f as---- | As `map1`@term, but with one more array.------ **Work:** *O(n)*------ **Span:** *O(1)*-let map2 'a 'b [n] 'x (f: a -> b -> x) (as: [n]a) (bs: [n]b): *[n]x =-  map (\(a, b) -> f a b) (zip2 as bs)---- | As `map2`@term, but with one more array.------ **Work:** *O(n)*------ **Span:** *O(1)*-let map3 'a 'b 'c [n] 'x (f: a -> b -> c -> x) (as: [n]a) (bs: [n]b) (cs: [n]c): *[n]x =-  map (\(a, b, c) -> f a b c) (zip3 as bs cs)---- | As `map3`@term, but with one more array.------ **Work:** *O(n)*------ **Span:** *O(1)*-let map4 'a 'b 'c 'd [n] 'x (f: a -> b -> c -> d -> x) (as: [n]a) (bs: [n]b) (cs: [n]c) (ds: [n]d): *[n]x =-  map (\(a, b, c, d) -> f a b c d) (zip4 as bs cs ds)---- | As `map4`@term, but with one more array.------ **Work:** *O(n)*------ **Span:** *O(1)*-let map5 'a 'b 'c 'd 'e [n] 'x (f: a -> b -> c -> d -> e -> x) (as: [n]a) (bs: [n]b) (cs: [n]c) (ds: [n]d) (es: [n]e): *[n]x =-  map (\(a, b, c, d, e) -> f a b c d e) (zip5 as bs cs ds es)---- | Reduce the array `as` with `op`, with `ne` as the neutral--- element for `op`.  The function `op` must be associative.  If--- it is not, the return value is unspecified.  If the value returned--- by the operator is an array, it must have the exact same size as--- the neutral element, and that must again have the same size as the--- elements of the input array.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let reduce 'a (op: a -> a -> a) (ne: a) (as: []a): a =-  intrinsics.reduce (op, ne, as)---- | As `reduce`, but the operator must also be commutative.  This is--- potentially faster than `reduce`.  For simple built-in operators,--- like addition, the compiler already knows that the operator is--- commutative, so plain `reduce`@term will work just as well.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let reduce_comm 'a (op: a -> a -> a) (ne: a) (as: []a): a =-  intrinsics.reduce_comm (op, ne, as)---- | `reduce_by_index dest f ne is as` returns `dest`, but with each--- element given by the indices of `is` updated by applying `f` to the--- current value in `dest` and the corresponding value in `as`.  The--- `ne` value must be a neutral element for `op`.  If `is` has--- duplicates, `f` may be applied multiple times, and hence must be--- associative and commutative.  Out-of-bounds indices in `is` are--- ignored.------ **Work:** *O(n)*------ **Span:** *O(n)* in the worst case (all updates to same position),--- but *O(1)* in the best case.------ In practice, the *O(n)* behaviour only occurs if *m* is also very--- large.-let reduce_by_index 'a [m] [n] (dest : *[m]a) (f : a -> a -> a) (ne : a) (is : [n]i32) (as : [n]a) : *[m]a =-  intrinsics.hist (1, dest, f, ne, is, as)---- | Inclusive prefix scan.  Has the same caveats with respect to--- associativity as `reduce`.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let scan [n] 'a (op: a -> a -> a) (ne: a) (as: [n]a): *[n]a =-  intrinsics.scan (op, ne, as)---- | Remove all those elements of `as` that do not satisfy the--- predicate `p`.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let filter 'a (p: a -> bool) (as: []a): *[]a =-  let (as', is) = intrinsics.partition (1, \x -> if p x then 0 else 1, as)-  in as'[:is[0]]---- | Split an array into those elements that satisfy the given--- predicate, and those that do not.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let partition [n] 'a (p: a -> bool) (as: [n]a): ([]a, []a) =-  let p' x = if p x then 0 else 1-  let (as', is) = intrinsics.partition (2, p', as)-  in (as'[0:is[0]], as'[is[0]:n])---- | Split an array by two predicates, producing three arrays.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let partition2 [n] 'a (p1: a -> bool) (p2: a -> bool) (as: [n]a): ([]a, []a, []a) =-  let p' x = if p1 x then 0 else if p2 x then 1 else 2-  let (as', is) = intrinsics.partition (3, p', as)-  in (as'[0:is[0]], as'[is[0]:is[0]+is[1]], as'[is[0]+is[1]:n])---- | `reduce_stream op f as` splits `as` into chunks, applies `f` to each--- of these in parallel, and uses `op` (which must be associative) to--- combine the per-chunk results into a final result.  The `i32`--- passed to `f` is the size of the chunk.  This SOAC is useful when--- `f` can be given a particularly work-efficient sequential--- implementation.  Operationally, we can imagine that `as` is divided--- among as many threads as necessary to saturate the machine, with--- each thread operating sequentially.------ A chunk may be empty, and `f 0 []` must produce the neutral element for--- `op`.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let reduce_stream 'a 'b (op: b -> b -> b) (f: i32 -> []a -> b) (as: []a): b =-  intrinsics.reduce_stream (op, f, as)---- | As `reduce_stream`@term, but the chunks do not necessarily--- correspond to subsequences of the original array (they may be--- interleaved).------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let reduce_stream_per 'a 'b (op: b -> b -> b) (f: i32 -> []a -> b) (as: []a): b =-  intrinsics.reduce_stream_per (op, f, as)---- | Similar to `reduce_stream`@term, except that each chunk must produce--- an array *of the same size*.  The per-chunk results are--- concatenated.------ **Work:** *O(n)*------ **Span:** *O(1)*-let map_stream 'a 'b (f: i32 -> []a -> []b) (as: []a): *[]b =-  intrinsics.map_stream (f, as)---- | Similar to `map_stream`@term, but the chunks do not necessarily--- correspond to subsequences of the original array (they may be--- interleaved).------ **Work:** *O(n)*------ **Span:** *O(1)*-let map_stream_per 'a 'b (f: i32 -> []a -> []b) (as: []a): *[]b =-  intrinsics.map_stream_per (f, as)---- | Return `true` if the given function returns `true` for all--- elements in the array.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let all 'a (f: a -> bool) (as: []a): bool =-  reduce (&&) true (map f as)---- | Return `true` if the given function returns `true` for any--- elements in the array.------ **Work:** *O(n)*------ **Span:** *O(log(n))*-let any 'a (f: a -> bool) (as: []a): bool =-  reduce (||) false (map f as)---- | The `scatter as is vs` expression calculates the equivalent of--- this imperative code:------ ```--- for index in 0..length is-1:---   i = is[index]---   v = vs[index]---   as[i] = v--- ```------ The `is` and `vs` arrays must have the same outer size.  `scatter`--- acts in-place and consumes the `as` array, returning a new array--- that has the same type and elements as `as`, except for the indices--- in `is`.  If `is` contains duplicates (i.e. several writes are--- performed to the same location), the result is unspecified.  It is--- not guaranteed that one of the duplicate writes will complete--- atomically - they may be interleaved.  See `reduce_by_index`@term--- for a function that can handle this case deterministically.------ This is technically not a second-order operation, but it is defined--- here because it is closely related to the SOACs.------ **Work:** *O(n)*------ **Span:** *O(1)*-let scatter 't [m] [n] (dest: *[m]t) (is: [n]i32) (vs: [n]t): *[m]t =-  intrinsics.scatter (dest, is, vs)
− futlib/zip.fut
@@ -1,58 +0,0 @@--- | Transforming arrays of tuples into tuples of arrays and back--- again.  These are generally very cheap operations, as the internal--- compiler representation is always tuples of arrays.---- The main reason this module exists is that we need it to define--- SOACs like `map2`@term@"/futlib/soacs".---- We need a map to define some of the zip variants, but this file is--- depended upon by soacs.fut.  So we just define a quick-and-dirty--- internal one here that uses the intrinsic version.-local let internal_map 'a [n] 'x (f: a -> x) (as: [n]a): [n]x =-  intrinsics.map (f, as)---- | Construct an array of pairs from two arrays.-let zip [n] 'a 'b (as: [n]a) (bs: [n]b): [n](a,b) =-  intrinsics.zip (as, bs)---- | Construct an array of pairs from two arrays.-let zip2 [n] 'a 'b (as: [n]a) (bs: [n]b): [n](a,b) =-  zip as bs---- | As `zip2`@term, but with one more array.-let zip3 [n] 'a 'b 'c (as: [n]a) (bs: [n]b) (cs: [n]c): [n](a,b,c) =-  internal_map (\(a,(b,c)) -> (a,b,c)) (zip as (zip2 bs cs))---- | As `zip3`@term, but with one more array.-let zip4 [n] 'a 'b 'c 'd (as: [n]a) (bs: [n]b) (cs: [n]c) (ds: [n]d): [n](a,b,c,d) =-  internal_map (\(a,(b,c,d)) -> (a,b,c,d)) (zip as (zip3 bs cs ds))---- | As `zip4`@term, but with one more array.-let zip5 [n] 'a 'b 'c 'd 'e (as: [n]a) (bs: [n]b) (cs: [n]c) (ds: [n]d) (es: [n]e): [n](a,b,c,d,e) =-  internal_map (\(a,(b,c,d,e)) -> (a,b,c,d,e)) (zip as (zip4 bs cs ds es))---- | Turn an array of pairs into two arrays.-let unzip [n] 'a 'b (xs: [n](a,b)): ([n]a, [n]b) =-  intrinsics.unzip xs---- | Turn an array of pairs into two arrays.-let unzip2 [n] 'a 'b (xs: [n](a,b)): ([n]a, [n]b) =-  unzip xs---- | As `unzip2`@term, but with one more array.-let unzip3 [n] 'a 'b 'c (xs: [n](a,b,c)): ([n]a, [n]b, [n]c) =-  let (as, bcs) = unzip (internal_map (\(a,b,c) -> (a,(b,c))) xs)-  let (bs, cs) = unzip bcs-  in (as, bs, cs)---- | As `unzip3`@term, but with one more array.-let unzip4 [n] 'a 'b 'c 'd (xs: [n](a,b,c,d)): ([n]a, [n]b, [n]c, [n]d) =-  let (as, bs, cds) = unzip3 (internal_map (\(a,b,c,d) -> (a,b,(c,d))) xs)-  let (cs, ds) = unzip cds-  in (as, bs, cs, ds)---- | As `unzip4`@term, but with one more array.-let unzip5 [n] 'a 'b 'c 'd 'e (xs: [n](a,b,c,d,e)): ([n]a, [n]b, [n]c, [n]d, [n]e) =-  let (as, bs, cs, des) = unzip4 (internal_map (\(a,b,c,d,e) -> (a,b,c,(d,e))) xs)-  let (ds, es) = unzip des-  in (as, bs, cs, ds, es)
− package.yaml
@@ -1,119 +0,0 @@-name: futhark-version: "0.14.1"-synopsis: An optimising compiler for a functional, array-oriented language.-description: |-  Futhark is a small programming language designed to be compiled to-  efficient parallel code. It is a statically typed, data-parallel,-  and purely functional array language in the ML family, and comes-  with a heavily optimising ahead-of-time compiler that presently-  generates GPU code via CUDA and OpenCL, although the language itself-  is hardware-agnostic.--  For more information, see the website at https://futhark-lang.org-homepage: https://futhark-lang.org-maintainer: Troels Henriksen athas@sigkill.dk-license: ISC-github: diku-dk/futhark-category: Language--ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists--extra-source-files:-  - rts/python/*.py-  - rts/c/*.h-  - rts/csharp/*.cs-  - rts/futhark-doc/*.css--  - futlib/*.fut--  - docs/Makefile-  - docs/conf.py-  - docs/index.rst-  - docs/man/*.rst-  - package.yaml--library:-  dependencies:-    - base >= 4.13 && < 5-    - array >= 0.4-    - binary >= 0.8.3-    - data-binary-ieee754 >= 0.1-    - vector >= 0.12-    - vector-binary-instances >= 0.2.2.0-    - containers >= 0.6.2.1-    - mtl >= 2.2.1-    - transformers >= 0.3-    - srcloc >= 0.4-    - language-c-quote >= 0.12-    - mainland-pretty >= 0.6.1-    - megaparsec >= 8.0.0-    - parser-combinators >= 1.0.0-    - regex-tdfa >= 1.2-    - filepath >= 1.4.1.1-    - dlist >= 0.6.0.1-    - bytestring >= 0.10.8-    - text >= 1.2.2.2-    - neat-interpolation >= 0.3-    - file-embed >= 0.0.9-    - directory >= 1.3.0.0-    - directory-tree >= 0.12.1-    - gitrev >= 1.2.0-    - parallel >= 3.2.1.0-    - blaze-html >= 0.9.0.1-    - template-haskell >= 2.11.1-    - process >= 1.4.3.0-    - markdown >= 0.1.16-    - zlib >= 0.6.1.2-    - versions >= 3.3.1-    - http-client >= 0.5.7.0-    - http-client-tls >= 0.3.5.1-    - http-conduit >= 2.2.4-    - process-extras >= 0.7.2-    - free >= 4.12.4-    - zip-archive >= 0.3.1.1-    - time >= 1.6.0.1-    - ansi-terminal >= 0.6.3.1-    - random-    - temporary-    - aeson >= 1.0.0.0-    - haskeline-    - utf8-string >= 1-    - terminal-size >= 0.3-    - unordered-containers >= 0.2.7--  build-tools:-    - alex-    - happy--  source-dirs: src--  other-modules:-    - Language.Futhark.Parser.Parser-    - Language.Futhark.Parser.Lexer-    - Paths_futhark--executables:-  futhark: &futhark-    main: src/futhark.hs-    dependencies:-      - base-      - futhark-      - text-    ghc-options: -threaded -rtsopts "-with-rtsopts=-N -qg"--tests:-  unit:-    source-dirs: unittests-    main: futhark_tests.hs-    dependencies:-      - base-      - futhark-      - containers-      - mtl-      - text-      - QuickCheck >= 2.8-      - tasty-      - tasty-quickcheck-      - tasty-hunit-      - megaparsec-      - parser-combinators
+ prelude/array.fut view
@@ -0,0 +1,134 @@+-- | Utility functions for arrays.++import "math"+import "soacs"+import "functional"+open import "zip" -- Rexport.++-- | The size of the outer dimension of an array.+let length [n] 't (_: [n]t) = n++-- | Is the array empty?+let null [n] 't (_: [n]t) = n == 0++-- | The first element of the array.+let head [n] 't (x: [n]t) = x[0]++-- | The last element of the array.+let last [n] 't (x: [n]t) = x[n-1]++-- | Everything but the first element of the array.+let tail [n] 't (x: [n]t) = x[1:]++-- | Everything but the last element of the array.+let init [n] 't (x: [n]t) = x[0:n-1]++-- | Take some number of elements from the head of the array.+let take [n] 't (i: i32) (x: [n]t): [i]t = x[0:i]++-- | Remove some number of elements from the head of the array.+let drop [n] 't (i: i32) (x: [n]t) = x[i:]++-- | Split an array at a given position.+let split [n] 't (i: i32) (xs: [n]t): ([i]t, []t) =+  (xs[:i] :> [i]t, xs[i:])++-- | Return the elements of the array in reverse order.+let reverse [n] 't (x: [n]t): [n]t = x[::-1] :> [n]t++-- | Concatenate two arrays.  Warning: never try to perform a reduction+-- with this operator; it will not work.+let (++) [n] [m] 't (xs: [n]t) (ys: [m]t): *[]t = intrinsics.concat (xs, ys)++-- | An old-fashioned way of saying `++`.+let concat [n] [m] 't (xs: [n]t) (ys: [m]t): *[]t = xs ++ ys++-- | Concatenation where the result has a predetermined size.  If the+-- provided size is wrong, the function will fail with a run-time+-- error.+let concat_to 't (n: i32) (xs: []t) (ys: []t): *[]t = xs ++ ys :> [n]t++-- | Rotate an array some number of elements to the left.  A negative+-- rotation amount is also supported.+--+-- For example, if `b==rotate r a`, then `b[x+r] = a[x]`.+let rotate [n] 't (r: i32) (xs: [n]t): [n]t = intrinsics.rotate (r, xs) :> [n]t++-- | Construct an array of consecutive integers of the given length,+-- starting at 0.+let iota (n: i32): *[n]i32 =+  i32.iota n :> [n]i32++-- | Construct an array comprising valid indexes into some other+-- array, starting at 0.+let indices [n] 't (_: [n]t) : *[n]i32 =+  iota n++-- | Construct an array of the given length containing the given+-- value.+let replicate 't (n: i32) (x: t): *[n]t =+  i32.replicate n x :> [n]t++-- | Copy a value.  The result will not alias anything.+let copy 't (a: t): *t =+  ([a])[0]++-- | Combines the outer two dimensions of an array.+let flatten [n][m] 't (xs: [n][m]t): []t =+  intrinsics.flatten xs++-- | Like `flatten`@term, but where the final size is known.  Fails at+-- runtime if the provided size is wrong.+let flatten_to [n][m] 't (l: i32) (xs: [n][m]t): [l]t =+  flatten xs :> [l]t++-- | Combines the outer three dimensions of an array.+let flatten_3d [n][m][l] 't (xs: [n][m][l]t): []t =+  flatten (flatten xs)++-- | Combines the outer four dimensions of an array.+let flatten_4d [n][m][l][k] 't (xs: [n][m][l][k]t): []t =+  flatten (flatten_3d xs)++-- | Splits the outer dimension of an array in two.+let unflatten [p] 't (n: i32) (m: i32) (xs: [p]t): [n][m]t =+  intrinsics.unflatten (n, m, xs) :> [n][m]t++-- | Splits the outer dimension of an array in three.+let unflatten_3d [p] 't (n: i32) (m: i32) (l: i32) (xs: [p]t): [n][m][l]t =+  unflatten n m (unflatten (n*m) l xs)++-- | Splits the outer dimension of an array in four.+let unflatten_4d [p] 't (n: i32) (m: i32) (l: i32) (k: i32) (xs: [p]t): [n][m][l][k]t =+  unflatten n m (unflatten_3d (n*m) l k xs)++let transpose [n] [m] 't (a: [n][m]t): [m][n]t =+  intrinsics.transpose a :> [m][n]t++-- | True if all of the input elements are true.  Produces true on an+-- empty array.+let and: []bool -> bool = all id++-- | True if any of the input elements are true.  Produces false on an+-- empty array.+let or: []bool -> bool = any id++-- | Perform a *sequential* left-fold of an array.+let foldl [n] 'a 'b (f: a -> b -> a) (acc: a) (bs: [n]b): a =+  loop acc for b in bs do f acc b++-- | Perform a *sequential* right-fold of an array.+let foldr [n] 'a 'b (f: b -> a -> a) (acc: a) (bs: [n]b): a =+  foldl (flip f) acc (reverse bs)++-- | Create a value for each point in a one-dimensional index space.+let tabulate 'a (n: i32) (f: i32 -> a): *[n]a =+  map1 f (iota n)++-- | Create a value for each point in a two-dimensional index space.+let tabulate_2d 'a (n: i32) (m: i32) (f: i32 -> i32 -> a): *[n][m]a =+  map1 (f >-> tabulate m) (iota n)++-- | Create a value for each point in a three-dimensional index space.+let tabulate_3d 'a (n: i32) (m: i32) (o: i32) (f: i32 -> i32 -> i32 -> a): *[n][m][o]a =+  map1 (f >-> tabulate_2d m o) (iota n)
+ prelude/functional.fut view
@@ -0,0 +1,57 @@+-- | Simple functional combinators.++-- | Left-to-right application.  Particularly useful for describing+-- computation pipelines:+--+-- ```+-- x |> f |> g |> h+-- ```+let (|>) '^a '^b (x: a) (f: a -> b): b = f x++-- | Right to left application.+let (<|) '^a '^b (f: a -> b) (x: a) = f x++-- | Function composition, with values flowing from left to right.+let (>->) '^a '^b '^c (f: a -> b) (g: b -> c) (x: a): c = g (f x)++-- | Function composition, with values flowing from right to left.+-- This is the same as the `∘` operator known from mathematics.+let (<-<) '^a '^b '^c (g: b -> c) (f: a -> b) (x: a): c = g (f x)++-- | Flip the arguments passed to a function.+--+-- ```+-- f x y == flip f y x+-- ```+let flip '^a '^b '^c (f: a -> b -> c) (b: b) (a: a): c =+  f a b++-- | Transform a function taking a pair into a function taking two+-- arguments.+let curry '^a '^b '^c (f: (a, b) -> c) (a: a) (b: b): c =+  f (a, b)++-- | Transform a function taking two arguments in a function taking a+-- pair.+let uncurry '^a '^b '^c (f: a -> b -> c) (a: a, b: b): c =+  f a b++-- | The constant function.+let const '^a '^b (x: a) (_: b): a = x++-- | The identity function.+let id '^a (x: a) = x++-- | Apply a function some number of times.+let iterate 'a (n: i32) (f: a -> a) (x: a) =+  loop x for _i < n do f x++-- | Keep applying `f` until `p` returns true for the input value.+-- May apply zero times.  *Note*: may not terminate.+let iterate_until 'a (p: a -> bool) (f: a -> a) (x: a) =+  loop x while ! (p x) do f x++-- | Keep applying `f` while `p` returns true for the input value.+-- May apply zero times.  *Note*: may not terminate.+let iterate_while 'a (p: a -> bool) (f: a -> a) (x: a) =+  loop x while p x do f x
+ prelude/math.fut view
@@ -0,0 +1,1060 @@+-- | Basic mathematical modules and functions.++import "soacs"++local let const 'a 'b (x: a) (_: b): a = x++-- | Describes types of values that can be created from the primitive+-- numeric types (and bool).+module type from_prim = {+  type t++  val i8: i8 -> t+  val i16: i16 -> t+  val i32: i32 -> t+  val i64: i64 -> t++  val u8: u8 -> t+  val u16: u16 -> t+  val u32: u32 -> t+  val u64: u64 -> t++  val f32: f32 -> t+  val f64: f64 -> t++  val bool: bool -> t+}++-- | A basic numeric module type that can be implemented for both+-- integers and rational numbers.+module type numeric = {+  include from_prim++  val +: t -> t -> t+  val -: t -> t -> t+  val *: t -> t -> t+  val /: t -> t -> t+  val %: t -> t -> t+  val **: t -> t -> t++  val to_i64: t -> i64++  val ==: t -> t -> bool+  val <: t -> t -> bool+  val >: t -> t -> bool+  val <=: t -> t -> bool+  val >=: t -> t -> bool+  val !=: t -> t -> bool++  val negate: t-> t+  val max: t -> t -> t+  val min: t -> t -> t++  val abs: t -> t++  val sgn: t -> t++  -- | The highest representable number.+  val highest: t++  -- | The lowest representable number.+  val lowest: t++  -- | Returns zero on empty input.+  val sum [n]: [n]t -> t++  -- | Returns one on empty input.+  val product [n]: [n]t -> t++  -- | Returns `lowest` on empty input.+  val maximum [n]: [n]t -> t+  -- | Returns `highest` on empty input.+  val minimum [n]: [n]t -> t+}++-- | An extension of `numeric`@mtype that provides facilities that are+-- only meaningful for integral types.+module type integral = {+  include numeric++  -- | Like `/`@term, but rounds towards zero.  This only matters when+  -- one of the operands is negative.  May be more efficient.+  val //: t -> t -> t+  -- | Like `%`@term, but rounds towards zero.  This only matters when+  -- one of the operands is negative.  May be more efficient.+  val %%: t -> t -> t++  val &: t -> t -> t+  val |: t -> t -> t+  val ^: t -> t -> t+  val !: t -> t++  val <<: t -> t -> t+  val >>: t -> t -> t+  val >>>: t -> t -> t++  val num_bits: i32+  val get_bit: i32 -> t -> i32+  val set_bit: i32 -> t -> i32 -> t++  -- | Count number of one bits.+  val popc: t -> i32++  -- | Computes `x * y` and returns the high half of the product of x+  -- and y.+  val mul_hi: (x: t) -> (y: t) -> t++  -- | Computes `mul_hi a b + c`, but perhaps in a more efficient way,+  -- depending on the target platform.+  val mad_hi: (a: t) -> (b: t) -> (c: t) -> t++  -- | Count number of zero bits preceding the most significant set+  -- bit.+  val clz: t -> i32+}++-- | An extension of `size`@mtype that further includes facilities for+-- constructing arrays where the size is provided as a value of the+-- given integral type.+module type size = {+  include integral++  val iota: t -> *[]t+  val replicate 'v: t -> v -> *[]v+}++-- | Numbers that model real numbers to some degree.+module type real = {+  include numeric++  val from_fraction: i32 -> i32 -> t+  val to_i32: t -> i32+  val to_i64: t -> i64+  val to_f64: t -> f64++  val sqrt: t -> t+  val exp: t -> t++  val sin: t -> t+  val cos: t -> t+  val tan: t -> t++  val asin: t -> t+  val acos: t -> t+  val atan: t -> t++  val sinh: t -> t+  val cosh: t -> t+  val tanh: t -> t++  val asinh: t -> t+  val acosh: t -> t+  val atanh: t -> t++  val atan2: t -> t -> t++  val gamma: t -> t+  val lgamma: t -> t+  -- | Linear interpolation.  The third argument must be in the range+  -- `[0,1]` or the results are unspecified.+  val lerp: t -> t -> t -> t++  -- | Natural logarithm.+  val log: t -> t+  -- | Base-2 logarithm.+  val log2: t -> t+  -- | Base-10 logarithm.+  val log10: t -> t++  val ceil : t -> t+  val floor : t -> t+  val trunc : t -> t++  -- | Computes `a*b+c`.  Depending on the compiler backend, this may+  -- be fused into a single operation that is faster but less+  -- accurate.  Do not confuse it with `fma`@term.+  val mad : (a: t) -> (b: t) -> (c: t) -> t++  -- | Computes `a*b+c`, with `a*b` being rounded with infinite+  -- precision.  Rounding of intermediate products shall not+  -- occur. Edge case behavior is per the IEEE 754-2008 standard.+  val fma : (a: t) -> (b: t) -> (c: t) -> t++  -- | Round to the nearest integer, with alfway cases rounded to the+  -- nearest even integer.  Note that this differs from `round()` in+  -- C, but matches more modern languages.+  val round : t -> t++  val isinf: t -> bool+  val isnan: t -> bool++  val inf: t+  val nan: t++  val pi: t+  val e: t+}++-- | An extension of `real`@mtype that further gives access to the+-- bitwise representation of the underlying number.  It is presumed+-- that this will be some form of IEEE float.+module type float = {+  include real++  -- | An unsigned integer type containing the same number of bits as+  -- 't'.+  type int_t++  val from_bits: int_t -> t+  val to_bits: t -> int_t++  val num_bits: i32+  val get_bit: i32 -> t -> i32+  val set_bit: i32 -> t -> i32 -> t+}++-- | Boolean numbers.  When converting from a number to `bool`, 0 is+-- considered `false` and any other value is `true`.+module bool: from_prim with t = bool = {+  type t = bool++  let i8  = intrinsics.itob_i8_bool+  let i16 = intrinsics.itob_i16_bool+  let i32 = intrinsics.itob_i32_bool+  let i64 = intrinsics.itob_i64_bool++  let u8  (x: u8)  = intrinsics.itob_i8_bool (intrinsics.sign_i8 x)+  let u16 (x: u16) = intrinsics.itob_i16_bool (intrinsics.sign_i16 x)+  let u32 (x: u32) = intrinsics.itob_i32_bool (intrinsics.sign_i32 x)+  let u64 (x: u64) = intrinsics.itob_i64_bool (intrinsics.sign_i64 x)++  let f32 (x: f32) = x != 0f32+  let f64 (x: f64) = x != 0f64++  let bool (x: bool) = x+}++module i8: (size with t = i8) = {+  type t = i8++  let (x: i8) + (y: i8) = intrinsics.add8 (x, y)+  let (x: i8) - (y: i8) = intrinsics.sub8 (x, y)+  let (x: i8) * (y: i8) = intrinsics.mul8 (x, y)+  let (x: i8) / (y: i8) = intrinsics.sdiv8 (x, y)+  let (x: i8) ** (y: i8) = intrinsics.pow8 (x, y)+  let (x: i8) % (y: i8) = intrinsics.smod8 (x, y)+  let (x: i8) // (y: i8) = intrinsics.squot8 (x, y)+  let (x: i8) %% (y: i8) = intrinsics.srem8 (x, y)++  let (x: i8) & (y: i8) = intrinsics.and8 (x, y)+  let (x: i8) | (y: i8) = intrinsics.or8 (x, y)+  let (x: i8) ^ (y: i8) = intrinsics.xor8 (x, y)+  let ! (x: i8) = intrinsics.complement8 x++  let (x: i8) << (y: i8) = intrinsics.shl8 (x, y)+  let (x: i8) >> (y: i8) = intrinsics.ashr8 (x, y)+  let (x: i8) >>> (y: i8) = intrinsics.lshr8 (x, y)++  let i8  (x: i8)  = intrinsics.sext_i8_i8 x+  let i16 (x: i16) = intrinsics.sext_i16_i8 x+  let i32 (x: i32) = intrinsics.sext_i32_i8 x+  let i64 (x: i64) = intrinsics.sext_i64_i8 x++  let u8  (x: u8)  = intrinsics.zext_i8_i8 (intrinsics.sign_i8 x)+  let u16 (x: u16) = intrinsics.zext_i16_i8 (intrinsics.sign_i16 x)+  let u32 (x: u32) = intrinsics.zext_i32_i8 (intrinsics.sign_i32 x)+  let u64 (x: u64) = intrinsics.zext_i64_i8 (intrinsics.sign_i64 x)++  let f32 (x: f32) = intrinsics.fptosi_f32_i8 x+  let f64 (x: f64) = intrinsics.fptosi_f64_i8 x++  let bool = intrinsics.btoi_bool_i8++  let to_i32(x: i8) = intrinsics.sext_i8_i32 x+  let to_i64(x: i8) = intrinsics.sext_i8_i64 x++  let (x: i8) == (y: i8) = intrinsics.eq_i8 (x, y)+  let (x: i8) < (y: i8) = intrinsics.slt8 (x, y)+  let (x: i8) > (y: i8) = intrinsics.slt8 (y, x)+  let (x: i8) <= (y: i8) = intrinsics.sle8 (x, y)+  let (x: i8) >= (y: i8) = intrinsics.sle8 (y, x)+  let (x: i8) != (y: i8) = intrinsics.! (x == y)++  let sgn (x: i8) = intrinsics.ssignum8 x+  let abs (x: i8) = intrinsics.abs8 x++  let negate (x: t) = -x+  let max (x: t) (y: t) = intrinsics.smax8 (x, y)+  let min (x: t) (y: t) = intrinsics.smin8 (x, y)++  let highest = 127i8+  let lowest = highest + 1i8++  let num_bits = 8+  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)+  let set_bit (bit: i32) (x: t) (b: i32) =+    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))+  let popc = intrinsics.popc8+  let mul_hi a b = intrinsics.mul_hi8 (i8 a, i8 b)+  let mad_hi a b c = intrinsics.mad_hi8 (i8 a, i8 b, i8 c)+  let clz = intrinsics.clz8++  let iota (n: i8) = 0i8..1i8..<n+  let replicate 'v (n: i8) (x: v) = map (const x) (iota n)++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module i16: (size with t = i16) = {+  type t = i16++  let (x: i16) + (y: i16) = intrinsics.add16 (x, y)+  let (x: i16) - (y: i16) = intrinsics.sub16 (x, y)+  let (x: i16) * (y: i16) = intrinsics.mul16 (x, y)+  let (x: i16) / (y: i16) = intrinsics.sdiv16 (x, y)+  let (x: i16) ** (y: i16) = intrinsics.pow16 (x, y)+  let (x: i16) % (y: i16) = intrinsics.smod16 (x, y)+  let (x: i16) // (y: i16) = intrinsics.squot16 (x, y)+  let (x: i16) %% (y: i16) = intrinsics.srem16 (x, y)++  let (x: i16) & (y: i16) = intrinsics.and16 (x, y)+  let (x: i16) | (y: i16) = intrinsics.or16 (x, y)+  let (x: i16) ^ (y: i16) = intrinsics.xor16 (x, y)+  let ! (x: i16) = intrinsics.complement16 x++  let (x: i16) << (y: i16) = intrinsics.shl16 (x, y)+  let (x: i16) >> (y: i16) = intrinsics.ashr16 (x, y)+  let (x: i16) >>> (y: i16) = intrinsics.lshr16 (x, y)++  let i8  (x: i8)  = intrinsics.sext_i8_i16 x+  let i16 (x: i16) = intrinsics.sext_i16_i16 x+  let i32 (x: i32) = intrinsics.sext_i32_i16 x+  let i64 (x: i64) = intrinsics.sext_i64_i16 x++  let u8  (x: u8)  = intrinsics.zext_i8_i16 (intrinsics.sign_i8 x)+  let u16 (x: u16) = intrinsics.zext_i16_i16 (intrinsics.sign_i16 x)+  let u32 (x: u32) = intrinsics.zext_i32_i16 (intrinsics.sign_i32 x)+  let u64 (x: u64) = intrinsics.zext_i64_i16 (intrinsics.sign_i64 x)++  let f32 (x: f32) = intrinsics.fptosi_f32_i16 x+  let f64 (x: f64) = intrinsics.fptosi_f64_i16 x++  let bool = intrinsics.btoi_bool_i16++  let to_i32(x: i16) = intrinsics.sext_i16_i32 x+  let to_i64(x: i16) = intrinsics.sext_i16_i64 x++  let (x: i16) == (y: i16) = intrinsics.eq_i16 (x, y)+  let (x: i16) < (y: i16) = intrinsics.slt16 (x, y)+  let (x: i16) > (y: i16) = intrinsics.slt16 (y, x)+  let (x: i16) <= (y: i16) = intrinsics.sle16 (x, y)+  let (x: i16) >= (y: i16) = intrinsics.sle16 (y, x)+  let (x: i16) != (y: i16) = intrinsics.! (x == y)++  let sgn (x: i16) = intrinsics.ssignum16 x+  let abs (x: i16) = intrinsics.abs16 x++  let negate (x: t) = -x+  let max (x: t) (y: t) = intrinsics.smax16 (x, y)+  let min (x: t) (y: t) = intrinsics.smin16 (x, y)++  let highest = 32767i16+  let lowest = highest + 1i16++  let num_bits = 16+  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)+  let set_bit (bit: i32) (x: t) (b: i32) =+    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))+  let popc = intrinsics.popc16+  let mul_hi a b = intrinsics.mul_hi16 (i16 a, i16 b)+  let mad_hi a b c = intrinsics.mad_hi16 (i16 a, i16 b, i16 c)+  let clz = intrinsics.clz16++  let iota (n: i16) = 0i16..1i16..<n+  let replicate 'v (n: i16) (x: v) = map (const x) (iota n)++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module i32: (size with t = i32) = {+  type t = i32++  let sign (x: u32) = intrinsics.sign_i32 x+  let unsign (x: i32) = intrinsics.unsign_i32 x++  let (x: i32) + (y: i32) = intrinsics.add32 (x, y)+  let (x: i32) - (y: i32) = intrinsics.sub32 (x, y)+  let (x: i32) * (y: i32) = intrinsics.mul32 (x, y)+  let (x: i32) / (y: i32) = intrinsics.sdiv32 (x, y)+  let (x: i32) ** (y: i32) = intrinsics.pow32 (x, y)+  let (x: i32) % (y: i32) = intrinsics.smod32 (x, y)+  let (x: i32) // (y: i32) = intrinsics.squot32 (x, y)+  let (x: i32) %% (y: i32) = intrinsics.srem32 (x, y)++  let (x: i32) & (y: i32) = intrinsics.and32 (x, y)+  let (x: i32) | (y: i32) = intrinsics.or32 (x, y)+  let (x: i32) ^ (y: i32) = intrinsics.xor32 (x, y)+  let ! (x: i32) = intrinsics.complement32 x++  let (x: i32) << (y: i32) = intrinsics.shl32 (x, y)+  let (x: i32) >> (y: i32) = intrinsics.ashr32 (x, y)+  let (x: i32) >>> (y: i32) = intrinsics.lshr32 (x, y)++  let i8  (x: i8)  = intrinsics.sext_i8_i32 x+  let i16 (x: i16) = intrinsics.sext_i16_i32 x+  let i32 (x: i32) = intrinsics.sext_i32_i32 x+  let i64 (x: i64) = intrinsics.sext_i64_i32 x++  let u8  (x: u8)  = intrinsics.zext_i8_i32 (intrinsics.sign_i8 x)+  let u16 (x: u16) = intrinsics.zext_i16_i32 (intrinsics.sign_i16 x)+  let u32 (x: u32) = intrinsics.zext_i32_i32 (intrinsics.sign_i32 x)+  let u64 (x: u64) = intrinsics.zext_i64_i32 (intrinsics.sign_i64 x)++  let f32 (x: f32) = intrinsics.fptosi_f32_i32 x+  let f64 (x: f64) = intrinsics.fptosi_f64_i32 x++  let bool = intrinsics.btoi_bool_i32++  let to_i32(x: i32) = intrinsics.sext_i32_i32 x+  let to_i64(x: i32) = intrinsics.sext_i32_i64 x++  let (x: i32) == (y: i32) = intrinsics.eq_i32 (x, y)+  let (x: i32) < (y: i32) = intrinsics.slt32 (x, y)+  let (x: i32) > (y: i32) = intrinsics.slt32 (y, x)+  let (x: i32) <= (y: i32) = intrinsics.sle32 (x, y)+  let (x: i32) >= (y: i32) = intrinsics.sle32 (y, x)+  let (x: i32) != (y: i32) = intrinsics.! (x == y)++  let sgn (x: i32) = intrinsics.ssignum32 x+  let abs (x: i32) = intrinsics.abs32 x++  let negate (x: t) = -x+  let max (x: t) (y: t) = intrinsics.smax32 (x, y)+  let min (x: t) (y: t) = intrinsics.smin32 (x, y)++  let highest = 2147483647+  let lowest = highest + 1++  let num_bits = 32+  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)+  let set_bit (bit: i32) (x: t) (b: i32) =+    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))+  let popc = intrinsics.popc32+  let mul_hi a b = intrinsics.mul_hi32 (i32 a, i32 b)+  let mad_hi a b c = intrinsics.mad_hi32 (i32 a, i32 b, i32 c)+  let clz = intrinsics.clz32++  let iota (n: i32) = 0..1..<n+  let replicate 'v (n: i32) (x: v) = map (const x) (iota n)++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module i64: (size with t = i64) = {+  type t = i64++  let sign (x: u64) = intrinsics.sign_i64 x+  let unsign (x: i64) = intrinsics.unsign_i64 x++  let (x: i64) + (y: i64) = intrinsics.add64 (x, y)+  let (x: i64) - (y: i64) = intrinsics.sub64 (x, y)+  let (x: i64) * (y: i64) = intrinsics.mul64 (x, y)+  let (x: i64) / (y: i64) = intrinsics.sdiv64 (x, y)+  let (x: i64) ** (y: i64) = intrinsics.pow64 (x, y)+  let (x: i64) % (y: i64) = intrinsics.smod64 (x, y)+  let (x: i64) // (y: i64) = intrinsics.squot64 (x, y)+  let (x: i64) %% (y: i64) = intrinsics.srem64 (x, y)++  let (x: i64) & (y: i64) = intrinsics.and64 (x, y)+  let (x: i64) | (y: i64) = intrinsics.or64 (x, y)+  let (x: i64) ^ (y: i64) = intrinsics.xor64 (x, y)+  let ! (x: i64) = intrinsics.complement64 x++  let (x: i64) << (y: i64) = intrinsics.shl64 (x, y)+  let (x: i64) >> (y: i64) = intrinsics.ashr64 (x, y)+  let (x: i64) >>> (y: i64) = intrinsics.lshr64 (x, y)++  let i8  (x: i8)  = intrinsics.sext_i8_i64 x+  let i16 (x: i16) = intrinsics.sext_i16_i64 x+  let i32 (x: i32) = intrinsics.sext_i32_i64 x+  let i64 (x: i64) = intrinsics.sext_i64_i64 x++  let u8  (x: u8)  = intrinsics.zext_i8_i64 (intrinsics.sign_i8 x)+  let u16 (x: u16) = intrinsics.zext_i16_i64 (intrinsics.sign_i16 x)+  let u32 (x: u32) = intrinsics.zext_i32_i64 (intrinsics.sign_i32 x)+  let u64 (x: u64) = intrinsics.zext_i64_i64 (intrinsics.sign_i64 x)++  let f32 (x: f32) = intrinsics.fptosi_f32_i64 x+  let f64 (x: f64) = intrinsics.fptosi_f64_i64 x++  let bool = intrinsics.btoi_bool_i64++  let to_i32(x: i64) = intrinsics.sext_i64_i32 x+  let to_i64(x: i64) = intrinsics.sext_i64_i64 x++  let (x: i64) == (y: i64) = intrinsics.eq_i64 (x, y)+  let (x: i64) < (y: i64) = intrinsics.slt64 (x, y)+  let (x: i64) > (y: i64) = intrinsics.slt64 (y, x)+  let (x: i64) <= (y: i64) = intrinsics.sle64 (x, y)+  let (x: i64) >= (y: i64) = intrinsics.sle64 (y, x)+  let (x: i64) != (y: i64) = intrinsics.! (x == y)++  let sgn (x: i64) = intrinsics.ssignum64 x+  let abs (x: i64) = intrinsics.abs64 x++  let negate (x: t) = -x+  let max (x: t) (y: t) = intrinsics.smax64 (x, y)+  let min (x: t) (y: t) = intrinsics.smin64 (x, y)++  let highest = 9223372036854775807i64+  let lowest = highest + 1i64++  let num_bits = 64+  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)+  let set_bit (bit: i32) (x: t) (b: i32) =+    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | intrinsics.zext_i32_i64 (b intrinsics.<< bit))+  let popc = intrinsics.popc64+  let mul_hi a b = intrinsics.mul_hi64 (i64 a, i64 b)+  let mad_hi a b c = intrinsics.mad_hi64 (i64 a, i64 b, i64 c)+  let clz = intrinsics.clz64++  let iota (n: i64) = 0i64..1i64..<n+  let replicate 'v (n: i64) (x: v) = map (const x) (iota n)++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module u8: (size with t = u8) = {+  type t = u8++  let sign (x: u8) = intrinsics.sign_i8 x+  let unsign (x: i8) = intrinsics.unsign_i8 x++  let (x: u8) + (y: u8) = unsign (intrinsics.add8 (sign x, sign y))+  let (x: u8) - (y: u8) = unsign (intrinsics.sub8 (sign x, sign y))+  let (x: u8) * (y: u8) = unsign (intrinsics.mul8 (sign x, sign y))+  let (x: u8) / (y: u8) = unsign (intrinsics.udiv8 (sign x, sign y))+  let (x: u8) ** (y: u8) = unsign (intrinsics.pow8 (sign x, sign y))+  let (x: u8) % (y: u8) = unsign (intrinsics.umod8 (sign x, sign y))+  let (x: u8) // (y: u8) = unsign (intrinsics.udiv8 (sign x, sign y))+  let (x: u8) %% (y: u8) = unsign (intrinsics.umod8 (sign x, sign y))++  let (x: u8) & (y: u8) = unsign (intrinsics.and8 (sign x, sign y))+  let (x: u8) | (y: u8) = unsign (intrinsics.or8 (sign x, sign y))+  let (x: u8) ^ (y: u8) = unsign (intrinsics.xor8 (sign x, sign y))+  let ! (x: u8) = unsign (intrinsics.complement8 (sign x))++  let (x: u8) << (y: u8) = unsign (intrinsics.shl8 (sign x, sign y))+  let (x: u8) >> (y: u8) = unsign (intrinsics.ashr8 (sign x, sign y))+  let (x: u8) >>> (y: u8) = unsign (intrinsics.lshr8 (sign x, sign y))++  let u8  (x: u8)  = unsign (i8.u8 x)+  let u16 (x: u16) = unsign (i8.u16 x)+  let u32 (x: u32) = unsign (i8.u32 x)+  let u64 (x: u64) = unsign (i8.u64 x)++  let i8  (x: i8)  = unsign (intrinsics.zext_i8_i8 x)+  let i16 (x: i16) = unsign (intrinsics.zext_i16_i8 x)+  let i32 (x: i32) = unsign (intrinsics.zext_i32_i8 x)+  let i64 (x: i64) = unsign (intrinsics.zext_i64_i8 x)++  let f32 (x: f32) = unsign (intrinsics.fptoui_f32_i8 x)+  let f64 (x: f64) = unsign (intrinsics.fptoui_f64_i8 x)++  let bool x = unsign (intrinsics.btoi_bool_i8 x)++  let to_i32(x: u8) = intrinsics.zext_i8_i32 (sign x)+  let to_i64(x: u8) = intrinsics.zext_i8_i64 (sign x)++  let (x: u8) == (y: u8) = intrinsics.eq_i8 (sign x, sign y)+  let (x: u8) < (y: u8) = intrinsics.ult8 (sign x, sign y)+  let (x: u8) > (y: u8) = intrinsics.ult8 (sign y, sign x)+  let (x: u8) <= (y: u8) = intrinsics.ule8 (sign x, sign y)+  let (x: u8) >= (y: u8) = intrinsics.ule8 (sign y, sign x)+  let (x: u8) != (y: u8) = intrinsics.! (x == y)++  let sgn (x: u8) = unsign (intrinsics.usignum8 (sign x))+  let abs (x: u8) = x++  let negate (x: t) = -x+  let max (x: t) (y: t) = unsign (intrinsics.umax8 (sign x, sign y))+  let min (x: t) (y: t) = unsign (intrinsics.umin8 (sign x, sign y))++  let highest = 255u8+  let lowest = 0u8++  let num_bits = 8+  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)+  let set_bit (bit: i32) (x: t) (b: i32) =+    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))+  let popc x = intrinsics.popc8 (sign x)+  let mul_hi a b = unsign (intrinsics.mul_hi8 (sign a, sign b))+  let mad_hi a b c = unsign (intrinsics.mad_hi8 (sign a, sign b, sign c))+  let clz x = intrinsics.clz8 (sign x)++  let iota (n: u8) = 0u8..1u8..<n+  let replicate 'v (n: u8) (x: v) = map (const x) (iota n)++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module u16: (size with t = u16) = {+  type t = u16++  let sign (x: u16) = intrinsics.sign_i16 x+  let unsign (x: i16) = intrinsics.unsign_i16 x++  let (x: u16) + (y: u16) = unsign (intrinsics.add16 (sign x, sign y))+  let (x: u16) - (y: u16) = unsign (intrinsics.sub16 (sign x, sign y))+  let (x: u16) * (y: u16) = unsign (intrinsics.mul16 (sign x, sign y))+  let (x: u16) / (y: u16) = unsign (intrinsics.udiv16 (sign x, sign y))+  let (x: u16) ** (y: u16) = unsign (intrinsics.pow16 (sign x, sign y))+  let (x: u16) % (y: u16) = unsign (intrinsics.umod16 (sign x, sign y))+  let (x: u16) // (y: u16) = unsign (intrinsics.udiv16 (sign x, sign y))+  let (x: u16) %% (y: u16) = unsign (intrinsics.umod16 (sign x, sign y))++  let (x: u16) & (y: u16) = unsign (intrinsics.and16 (sign x, sign y))+  let (x: u16) | (y: u16) = unsign (intrinsics.or16 (sign x, sign y))+  let (x: u16) ^ (y: u16) = unsign (intrinsics.xor16 (sign x, sign y))+  let ! (x: u16) = unsign (intrinsics.complement16 (sign x))++  let (x: u16) << (y: u16) = unsign (intrinsics.shl16 (sign x, sign y))+  let (x: u16) >> (y: u16) = unsign (intrinsics.ashr16 (sign x, sign y))+  let (x: u16) >>> (y: u16) = unsign (intrinsics.lshr16 (sign x, sign y))++  let u8  (x: u8)  = unsign (i16.u8 x)+  let u16 (x: u16) = unsign (i16.u16 x)+  let u32 (x: u32) = unsign (i16.u32 x)+  let u64 (x: u64) = unsign (i16.u64 x)++  let i8  (x: i8)  = unsign (intrinsics.zext_i8_i16 x)+  let i16 (x: i16) = unsign (intrinsics.zext_i16_i16 x)+  let i32 (x: i32) = unsign (intrinsics.zext_i32_i16 x)+  let i64 (x: i64) = unsign (intrinsics.zext_i64_i16 x)++  let f32 (x: f32) = unsign (intrinsics.fptoui_f32_i16 x)+  let f64 (x: f64) = unsign (intrinsics.fptoui_f64_i16 x)++  let bool x = unsign (intrinsics.btoi_bool_i16 x)++  let to_i32(x: u16) = intrinsics.zext_i16_i32 (sign x)+  let to_i64(x: u16) = intrinsics.zext_i16_i64 (sign x)++  let (x: u16) == (y: u16) = intrinsics.eq_i16 (sign x, sign y)+  let (x: u16) < (y: u16) = intrinsics.ult16 (sign x, sign y)+  let (x: u16) > (y: u16) = intrinsics.ult16 (sign y, sign x)+  let (x: u16) <= (y: u16) = intrinsics.ule16 (sign x, sign y)+  let (x: u16) >= (y: u16) = intrinsics.ule16 (sign y, sign x)+  let (x: u16) != (y: u16) = intrinsics.! (x == y)++  let sgn (x: u16) = unsign (intrinsics.usignum16 (sign x))+  let abs (x: u16) = x++  let negate (x: t) = -x+  let max (x: t) (y: t) = unsign (intrinsics.umax16 (sign x, sign y))+  let min (x: t) (y: t) = unsign (intrinsics.umin16 (sign x, sign y))++  let highest = 65535u16+  let lowest = 0u16++  let num_bits = 16+  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)+  let set_bit (bit: i32) (x: t) (b: i32) =+    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))+  let popc x = intrinsics.popc16 (sign x)+  let mul_hi a b = unsign (intrinsics.mul_hi16 (sign a, sign b))+  let mad_hi a b c = unsign (intrinsics.mad_hi16 (sign a, sign b, sign c))+  let clz x = intrinsics.clz16 (sign x)++  let iota (n: u16) = 0u16..1u16..<n+  let replicate 'v (n: u16) (x: v) = map (const x) (iota n)++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module u32: (size with t = u32) = {+  type t = u32++  let sign (x: u32) = intrinsics.sign_i32 x+  let unsign (x: i32) = intrinsics.unsign_i32 x++  let (x: u32) + (y: u32) = unsign (intrinsics.add32 (sign x, sign y))+  let (x: u32) - (y: u32) = unsign (intrinsics.sub32 (sign x, sign y))+  let (x: u32) * (y: u32) = unsign (intrinsics.mul32 (sign x, sign y))+  let (x: u32) / (y: u32) = unsign (intrinsics.udiv32 (sign x, sign y))+  let (x: u32) ** (y: u32) = unsign (intrinsics.pow32 (sign x, sign y))+  let (x: u32) % (y: u32) = unsign (intrinsics.umod32 (sign x, sign y))+  let (x: u32) // (y: u32) = unsign (intrinsics.udiv32 (sign x, sign y))+  let (x: u32) %% (y: u32) = unsign (intrinsics.umod32 (sign x, sign y))++  let (x: u32) & (y: u32) = unsign (intrinsics.and32 (sign x, sign y))+  let (x: u32) | (y: u32) = unsign (intrinsics.or32 (sign x, sign y))+  let (x: u32) ^ (y: u32) = unsign (intrinsics.xor32 (sign x, sign y))+  let ! (x: u32) = unsign (intrinsics.complement32 (sign x))++  let (x: u32) << (y: u32) = unsign (intrinsics.shl32 (sign x, sign y))+  let (x: u32) >> (y: u32) = unsign (intrinsics.ashr32 (sign x, sign y))+  let (x: u32) >>> (y: u32) = unsign (intrinsics.lshr32 (sign x, sign y))++  let u8  (x: u8)  = unsign (i32.u8 x)+  let u16 (x: u16) = unsign (i32.u16 x)+  let u32 (x: u32) = unsign (i32.u32 x)+  let u64 (x: u64) = unsign (i32.u64 x)++  let i8  (x: i8)  = unsign (intrinsics.zext_i8_i32 x)+  let i16 (x: i16) = unsign (intrinsics.zext_i16_i32 x)+  let i32 (x: i32) = unsign (intrinsics.zext_i32_i32 x)+  let i64 (x: i64) = unsign (intrinsics.zext_i64_i32 x)++  let f32 (x: f32) = unsign (intrinsics.fptoui_f32_i32 x)+  let f64 (x: f64) = unsign (intrinsics.fptoui_f64_i32 x)++  let bool x = unsign (intrinsics.btoi_bool_i32 x)++  let to_i32(x: u32) = intrinsics.zext_i32_i32 (sign x)+  let to_i64(x: u32) = intrinsics.zext_i32_i64 (sign x)++  let (x: u32) == (y: u32) = intrinsics.eq_i32 (sign x, sign y)+  let (x: u32) < (y: u32) = intrinsics.ult32 (sign x, sign y)+  let (x: u32) > (y: u32) = intrinsics.ult32 (sign y, sign x)+  let (x: u32) <= (y: u32) = intrinsics.ule32 (sign x, sign y)+  let (x: u32) >= (y: u32) = intrinsics.ule32 (sign y, sign x)+  let (x: u32) != (y: u32) = intrinsics.! (x == y)++  let sgn (x: u32) = unsign (intrinsics.usignum32 (sign x))+  let abs (x: u32) = x++  let highest = 4294967295u32+  let lowest = highest + 1u32++  let negate (x: t) = -x+  let max (x: t) (y: t) = unsign (intrinsics.umax32 (sign x, sign y))+  let min (x: t) (y: t) = unsign (intrinsics.umin32 (sign x, sign y))++  let num_bits = 32+  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)+  let set_bit (bit: i32) (x: t) (b: i32) =+    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))+  let popc x = intrinsics.popc32 (sign x)+  let mul_hi a b = unsign (intrinsics.mul_hi32 (sign a, sign b))+  let mad_hi a b c = unsign (intrinsics.mad_hi32 (sign a, sign b, sign c))+  let clz x = intrinsics.clz32 (sign x)++  let iota (n: u32) = 0u32..1u32..<n+  let replicate 'v (n: u32) (x: v) = map (const x) (iota n)++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module u64: (size with t = u64) = {+  type t = u64++  let sign (x: u64) = intrinsics.sign_i64 x+  let unsign (x: i64) = intrinsics.unsign_i64 x++  let (x: u64) + (y: u64) = unsign (intrinsics.add64 (sign x, sign y))+  let (x: u64) - (y: u64) = unsign (intrinsics.sub64 (sign x, sign y))+  let (x: u64) * (y: u64) = unsign (intrinsics.mul64 (sign x, sign y))+  let (x: u64) / (y: u64) = unsign (intrinsics.udiv64 (sign x, sign y))+  let (x: u64) ** (y: u64) = unsign (intrinsics.pow64 (sign x, sign y))+  let (x: u64) % (y: u64) = unsign (intrinsics.umod64 (sign x, sign y))+  let (x: u64) // (y: u64) = unsign (intrinsics.udiv64 (sign x, sign y))+  let (x: u64) %% (y: u64) = unsign (intrinsics.umod64 (sign x, sign y))++  let (x: u64) & (y: u64) = unsign (intrinsics.and64 (sign x, sign y))+  let (x: u64) | (y: u64) = unsign (intrinsics.or64 (sign x, sign y))+  let (x: u64) ^ (y: u64) = unsign (intrinsics.xor64 (sign x, sign y))+  let ! (x: u64) = unsign (intrinsics.complement64 (sign x))++  let (x: u64) << (y: u64) = unsign (intrinsics.shl64 (sign x, sign y))+  let (x: u64) >> (y: u64) = unsign (intrinsics.ashr64 (sign x, sign y))+  let (x: u64) >>> (y: u64) = unsign (intrinsics.lshr64 (sign x, sign y))++  let u8  (x: u8)  = unsign (i64.u8 x)+  let u16 (x: u16) = unsign (i64.u16 x)+  let u32 (x: u32) = unsign (i64.u32 x)+  let u64 (x: u64) = unsign (i64.u64 x)++  let i8 (x: i8)   = unsign (intrinsics.zext_i8_i64 x)+  let i16 (x: i16) = unsign (intrinsics.zext_i16_i64 x)+  let i32 (x: i32) = unsign (intrinsics.zext_i32_i64 x)+  let i64 (x: i64) = unsign (intrinsics.zext_i64_i64 x)++  let f32 (x: f32) = unsign (intrinsics.fptoui_f32_i64 x)+  let f64 (x: f64) = unsign (intrinsics.fptoui_f64_i64 x)++  let bool x = unsign (intrinsics.btoi_bool_i64 x)++  let to_i32(x: u64) = intrinsics.zext_i64_i32 (sign x)+  let to_i64(x: u64) = intrinsics.zext_i64_i64 (sign x)++  let (x: u64) == (y: u64) = intrinsics.eq_i64 (sign x, sign y)+  let (x: u64) < (y: u64) = intrinsics.ult64 (sign x, sign y)+  let (x: u64) > (y: u64) = intrinsics.ult64 (sign y, sign x)+  let (x: u64) <= (y: u64) = intrinsics.ule64 (sign x, sign y)+  let (x: u64) >= (y: u64) = intrinsics.ule64 (sign y, sign x)+  let (x: u64) != (y: u64) = intrinsics.! (x == y)++  let sgn (x: u64) = unsign (intrinsics.usignum64 (sign x))+  let abs (x: u64) = x++  let negate (x: t) = -x+  let max (x: t) (y: t) = unsign (intrinsics.umax64 (sign x, sign y))+  let min (x: t) (y: t) = unsign (intrinsics.umin64 (sign x, sign y))++  let highest = 18446744073709551615u64+  let lowest = highest + 1u64++  let num_bits = 64+  let get_bit (bit: i32) (x: t) = to_i32 ((x >> i32 bit) & i32 1)+  let set_bit (bit: i32) (x: t) (b: i32) =+    ((x & i32 (intrinsics.!(1 intrinsics.<< bit))) | i32 (b intrinsics.<< bit))+  let popc x = intrinsics.popc64 (sign x)+  let mul_hi a b = unsign (intrinsics.mul_hi64 (sign a, sign b))+  let mad_hi a b c = unsign (intrinsics.mad_hi64 (sign a, sign b, sign c))+  let clz x = intrinsics.clz64 (sign x)++  let iota (n: u64) = 0u64..1u64..<n+  let replicate 'v (n: u64) (x: v) = map (const x) (iota n)++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module f64: (float with t = f64 with int_t = u64) = {+  type t = f64+  type int_t = u64++  module i64m = i64+  module u64m = u64++  let (x: f64) + (y: f64) = intrinsics.fadd64 (x, y)+  let (x: f64) - (y: f64) = intrinsics.fsub64 (x, y)+  let (x: f64) * (y: f64) = intrinsics.fmul64 (x, y)+  let (x: f64) / (y: f64) = intrinsics.fdiv64 (x, y)+  let (x: f64) % (y: f64) = intrinsics.fmod64 (x, y)+  let (x: f64) ** (y: f64) = intrinsics.fpow64 (x, y)++  let u8  (x: u8)  = intrinsics.uitofp_i8_f64  (i8.u8 x)+  let u16 (x: u16) = intrinsics.uitofp_i16_f64 (i16.u16 x)+  let u32 (x: u32) = intrinsics.uitofp_i32_f64 (i32.u32 x)+  let u64 (x: u64) = intrinsics.uitofp_i64_f64 (i64.u64 x)++  let i8 (x: i8) = intrinsics.sitofp_i8_f64 x+  let i16 (x: i16) = intrinsics.sitofp_i16_f64 x+  let i32 (x: i32) = intrinsics.sitofp_i32_f64 x+  let i64 (x: i64) = intrinsics.sitofp_i64_f64 x++  let f32 (x: f32) = intrinsics.fpconv_f32_f64 x+  let f64 (x: f64) = intrinsics.fpconv_f64_f64 x++  let bool (x: bool) = if x then 1f64 else 0f64++  let from_fraction (x: i32) (y: i32) = i32 x / i32 y+  let to_i32 (x: f64) = intrinsics.fptosi_f64_i32 x+  let to_i64 (x: f64) = intrinsics.fptosi_f64_i64 x+  let to_f64 (x: f64) = x++  let (x: f64) == (y: f64) = intrinsics.eq_f64 (x, y)+  let (x: f64) < (y: f64) = intrinsics.lt64 (x, y)+  let (x: f64) > (y: f64) = intrinsics.lt64 (y, x)+  let (x: f64) <= (y: f64) = intrinsics.le64 (x, y)+  let (x: f64) >= (y: f64) = intrinsics.le64 (y, x)+  let (x: f64) != (y: f64) = intrinsics.! (x == y)++  let negate (x: t) = -x+  let max (x: t) (y: t) = intrinsics.fmax64 (x, y)+  let min (x: t) (y: t) = intrinsics.fmin64 (x, y)++  let sgn (x: f64) = if      x < 0f64  then -1f64+                     else if x == 0f64 then  0f64+                     else                    1f64+  let abs (x: f64) = intrinsics.fabs64 x++  let sqrt (x: f64) = intrinsics.sqrt64 x++  let log (x: f64) = intrinsics.log64 x+  let log2 (x: f64) = intrinsics.log2_64 x+  let log10 (x: f64) = intrinsics.log10_64 x+  let exp (x: f64) = intrinsics.exp64 x+  let sin (x: f64) = intrinsics.sin64 x+  let cos (x: f64) = intrinsics.cos64 x+  let tan (x: f64) = intrinsics.tan64 x+  let acos (x: f64) = intrinsics.acos64 x+  let asin (x: f64) = intrinsics.asin64 x+  let atan (x: f64) = intrinsics.atan64 x+  let sinh (x: f64) = intrinsics.sinh64 x+  let cosh (x: f64) = intrinsics.cosh64 x+  let tanh (x: f64) = intrinsics.tanh64 x+  let acosh (x: f64) = intrinsics.acosh64 x+  let asinh (x: f64) = intrinsics.asinh64 x+  let atanh (x: f64) = intrinsics.atanh64 x+  let atan2 (x: f64) (y: f64) = intrinsics.atan2_64 (x, y)+  let gamma = intrinsics.gamma64+  let lgamma = intrinsics.lgamma64++  let lerp v0 v1 t = intrinsics.lerp64 (v0,v1,t)+  let fma a b c = intrinsics.fma64 (a,b,c)+  let mad a b c = intrinsics.mad64 (a,b,c)++  let ceil = intrinsics.ceil64+  let floor = intrinsics.floor64+  let trunc (x: f64) : f64 = i64 (i64m.f64 x)++  let round = intrinsics.round64++  let to_bits (x: f64): u64 = u64m.i64 (intrinsics.to_bits64 x)+  let from_bits (x: u64): f64 = intrinsics.from_bits64 (intrinsics.sign_i64 x)++  let num_bits = 64+  let get_bit (bit: i32) (x: t) = u64m.get_bit bit (to_bits x)+  let set_bit (bit: i32) (x: t) (b: i32) = from_bits (u64m.set_bit bit (to_bits x) b)++  let isinf (x: f64) = intrinsics.isinf64 x+  let isnan (x: f64) = intrinsics.isnan64 x++  let inf = 1f64 / 0f64+  let nan = 0f64 / 0f64++  let highest = inf+  let lowest = -inf++  let pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062f64+  let e = 2.718281828459045235360287471352662497757247093699959574966967627724076630353f64++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}++module f32: (float with t = f32 with int_t = u32) = {+  type t = f32+  type int_t = u32++  module i32m = i32+  module u32m = u32+  module f64m = f64++  let (x: f32) + (y: f32) = intrinsics.fadd32 (x, y)+  let (x: f32) - (y: f32) = intrinsics.fsub32 (x, y)+  let (x: f32) * (y: f32) = intrinsics.fmul32 (x, y)+  let (x: f32) / (y: f32) = intrinsics.fdiv32 (x, y)+  let (x: f32) % (y: f32) = intrinsics.fmod32 (x, y)+  let (x: f32) ** (y: f32) = intrinsics.fpow32 (x, y)++  let u8  (x: u8)  = intrinsics.uitofp_i8_f32  (i8.u8 x)+  let u16 (x: u16) = intrinsics.uitofp_i16_f32 (i16.u16 x)+  let u32 (x: u32) = intrinsics.uitofp_i32_f32 (i32.u32 x)+  let u64 (x: u64) = intrinsics.uitofp_i64_f32 (i64.u64 x)++  let i8 (x: i8) = intrinsics.sitofp_i8_f32 x+  let i16 (x: i16) = intrinsics.sitofp_i16_f32 x+  let i32 (x: i32) = intrinsics.sitofp_i32_f32 x+  let i64 (x: i64) = intrinsics.sitofp_i64_f32 x++  let f32 (x: f32) = intrinsics.fpconv_f32_f32 x+  let f64 (x: f64) = intrinsics.fpconv_f64_f32 x++  let bool (x: bool) = if x then 1f32 else 0f32++  let from_fraction (x: i32) (y: i32) = i32 x / i32 y+  let to_i32 (x: f32) = intrinsics.fptosi_f32_i32 x+  let to_i64 (x: f32) = intrinsics.fptosi_f32_i64 x+  let to_f64 (x: f32) = intrinsics.fpconv_f32_f64 x++  let (x: f32) == (y: f32) = intrinsics.eq_f32 (x, y)+  let (x: f32) < (y: f32) = intrinsics.lt32 (x, y)+  let (x: f32) > (y: f32) = intrinsics.lt32 (y, x)+  let (x: f32) <= (y: f32) = intrinsics.le32 (x, y)+  let (x: f32) >= (y: f32) = intrinsics.le32 (y, x)+  let (x: f32) != (y: f32) = intrinsics.! (x == y)++  let negate (x: t) = -x+  let max (x: t) (y: t) = intrinsics.fmax32 (x, y)+  let min (x: t) (y: t) = intrinsics.fmin32 (x, y)++  let sgn (x: f32) = if      x < 0f32  then -1f32+                     else if x == 0f32 then  0f32+                     else                    1f32+  let abs (x: f32) = intrinsics.fabs32 x++  let sqrt (x: f32) = intrinsics.sqrt32 x++  let log (x: f32) = intrinsics.log32 x+  let log2 (x: f32) = intrinsics.log2_32 x+  let log10 (x: f32) = intrinsics.log10_32 x+  let exp (x: f32) = intrinsics.exp32 x+  let sin (x: f32) = intrinsics.sin32 x+  let cos (x: f32) = intrinsics.cos32 x+  let tan (x: f32) = intrinsics.tan32 x+  let acos (x: f32) = intrinsics.acos32 x+  let asin (x: f32) = intrinsics.asin32 x+  let atan (x: f32) = intrinsics.atan32 x+  let sinh (x: f32) = intrinsics.sinh32 x+  let cosh (x: f32) = intrinsics.cosh32 x+  let tanh (x: f32) = intrinsics.tanh32 x+  let acosh (x: f32) = intrinsics.acosh32 x+  let asinh (x: f32) = intrinsics.asinh32 x+  let atanh (x: f32) = intrinsics.atanh32 x+  let atan2 (x: f32) (y: f32) = intrinsics.atan2_32 (x, y)+  let gamma = intrinsics.gamma32+  let lgamma = intrinsics.lgamma32++  let lerp v0 v1 t = intrinsics.lerp32 (v0,v1,t)+  let fma a b c = intrinsics.fma32 (a,b,c)+  let mad a b c = intrinsics.mad32 (a,b,c)++  let ceil = intrinsics.ceil32+  let floor = intrinsics.floor32+  let trunc (x: f32) : f32 = i32 (i32m.f32 x)++  let round = intrinsics.round32++  let to_bits (x: f32): u32 = u32m.i32 (intrinsics.to_bits32 x)+  let from_bits (x: u32): f32 = intrinsics.from_bits32 (intrinsics.sign_i32 x)++  let num_bits = 32+  let get_bit (bit: i32) (x: t) = u32m.get_bit bit (to_bits x)+  let set_bit (bit: i32) (x: t) (b: i32) = from_bits (u32m.set_bit bit (to_bits x) b)++  let isinf (x: f32) = intrinsics.isinf32 x+  let isnan (x: f32) = intrinsics.isnan32 x++  let inf = 1f32 / 0f32+  let nan = 0f32 / 0f32++  let highest = inf+  let lowest = -inf++  let pi = f64 f64m.pi+  let e = f64 f64m.e++  let sum = reduce (+) (i32 0)+  let product = reduce (*) (i32 1)+  let maximum = reduce max lowest+  let minimum = reduce min highest+}
+ prelude/prelude.fut view
@@ -0,0 +1,34 @@+-- | The default prelude that is implicitly available in all Futhark+-- files.++open import "soacs"+open import "array"+open import "math"+open import "functional"++-- | Create single-precision float from integer.+let r32 (x: i32): f32 = f32.i32 x+-- | Create integer from single-precision float.+let t32 (x: f32): i32 = i32.f32 x++-- | Create double-precision float from integer.+let r64 (x: i32): f64 = f64.i32 x+-- | Create integer from double-precision float.+let t64 (x: f64): i32 = i32.f64 x++-- | Semantically just identity, but serves as an optimisation+-- inhibitor.  The compiler will treat this function as a black box.+-- You can use this to work around optimisation deficiencies (or+-- bugs), although it should hopefully rarely be necessary.+let opaque 't (x: t): t =+  intrinsics.opaque x++-- | Semantically just identity, but when run in the interpreter, the+-- argument value will be printed.+let trace 't (x: t): t =+  intrinsics.trace x++-- | Semantically just identity, but acts as a break point in+-- `futhark repl`.+let break 't (x: t): t =+  intrinsics.break x
+ prelude/soacs.fut view
@@ -0,0 +1,256 @@+-- | Various Second-Order Array Combinators that are operationally+-- parallel in a way that can be exploited by the compiler.+--+-- The functions here are all recognised specially by the compiler (or+-- built on those that are).  The asymptotic [work and+-- span](https://en.wikipedia.org/wiki/Analysis_of_parallel_algorithms)+-- is provided for each function, but note that this easily hides very+-- substantial constant factors.  For example, `scan`@term is *much*+-- slower than `reduce`@term, although they have the same asymptotic+-- complexity.+--+-- *Reminder on terminology*: A function `op` is said to be+-- *associative* if+--+--     (x `op` y) `op` z == x `op` (y `op` z)+--+-- for all `x`, `y`, `z`.  Similarly, it is *commutative* if+--+--     x `op` y == y `op` x+--+-- The value `o` is a *neutral element* if+--+--     x `op` o == o `op` x == x+--+-- for any `x`.++-- Implementation note: many of these definitions contain dynamically+-- checked size casts.  These will be removed by the compiler, but are+-- necessary for the type checker, as the 'intrinsics' functions are+-- not size-dependently typed.++import "zip"++-- | Apply the given function to each element of an array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let map 'a [n] 'x (f: a -> x) (as: [n]a): *[n]x =+  intrinsics.map (f, as) :> *[n]x++-- | Apply the given function to each element of a single array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let map1 'a [n] 'x (f: a -> x) (as: [n]a): *[n]x =+  map f as++-- | As `map1`@term, but with one more array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let map2 'a 'b [n] 'x (f: a -> b -> x) (as: [n]a) (bs: [n]b): *[n]x =+  map (\(a, b) -> f a b) (zip2 as bs)++-- | As `map2`@term, but with one more array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let map3 'a 'b 'c [n] 'x (f: a -> b -> c -> x) (as: [n]a) (bs: [n]b) (cs: [n]c): *[n]x =+  map (\(a, b, c) -> f a b c) (zip3 as bs cs)++-- | As `map3`@term, but with one more array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let map4 'a 'b 'c 'd [n] 'x (f: a -> b -> c -> d -> x) (as: [n]a) (bs: [n]b) (cs: [n]c) (ds: [n]d): *[n]x =+  map (\(a, b, c, d) -> f a b c d) (zip4 as bs cs ds)++-- | As `map4`@term, but with one more array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let map5 'a 'b 'c 'd 'e [n] 'x (f: a -> b -> c -> d -> e -> x) (as: [n]a) (bs: [n]b) (cs: [n]c) (ds: [n]d) (es: [n]e): *[n]x =+  map (\(a, b, c, d, e) -> f a b c d e) (zip5 as bs cs ds es)++-- | Reduce the array `as` with `op`, with `ne` as the neutral+-- element for `op`.  The function `op` must be associative.  If+-- it is not, the return value is unspecified.  If the value returned+-- by the operator is an array, it must have the exact same size as+-- the neutral element, and that must again have the same size as the+-- elements of the input array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let reduce [n] 'a (op: a -> a -> a) (ne: a) (as: [n]a): a =+  intrinsics.reduce (op, ne, as)++-- | As `reduce`, but the operator must also be commutative.  This is+-- potentially faster than `reduce`.  For simple built-in operators,+-- like addition, the compiler already knows that the operator is+-- commutative, so plain `reduce`@term will work just as well.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let reduce_comm [n] 'a (op: a -> a -> a) (ne: a) (as: [n]a): a =+  intrinsics.reduce_comm (op, ne, as)++-- | `reduce_by_index dest f ne is as` returns `dest`, but with each+-- element given by the indices of `is` updated by applying `f` to the+-- current value in `dest` and the corresponding value in `as`.  The+-- `ne` value must be a neutral element for `op`.  If `is` has+-- duplicates, `f` may be applied multiple times, and hence must be+-- associative and commutative.  Out-of-bounds indices in `is` are+-- ignored.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(n)* in the worst case (all updates to same position),+-- but *O(1)* in the best case.+--+-- In practice, the *O(n)* behaviour only occurs if *m* is also very+-- large.+let reduce_by_index 'a [m] [n] (dest : *[m]a) (f : a -> a -> a) (ne : a) (is : [n]i32) (as : [n]a) : *[m]a =+  intrinsics.hist (1, dest, f, ne, is, as) :> *[m]a++-- | Inclusive prefix scan.  Has the same caveats with respect to+-- associativity as `reduce`.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let scan [n] 'a (op: a -> a -> a) (ne: a) (as: [n]a): *[n]a =+  intrinsics.scan (op, ne, as) :> *[n]a++-- | Remove all those elements of `as` that do not satisfy the+-- predicate `p`.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let filter [n] 'a (p: a -> bool) (as: [n]a): *[]a =+  let (as', is) = intrinsics.partition (1, \x -> if p x then 0 else 1, as)+  in as'[:is[0]]++-- | Split an array into those elements that satisfy the given+-- predicate, and those that do not.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let partition [n] 'a (p: a -> bool) (as: [n]a): ([]a, []a) =+  let p' x = if p x then 0 else 1+  let (as', is) = intrinsics.partition (2, p', as)+  in (as'[0:is[0]], as'[is[0]:n])++-- | Split an array by two predicates, producing three arrays.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let partition2 [n] 'a (p1: a -> bool) (p2: a -> bool) (as: [n]a): ([]a, []a, []a) =+  let p' x = if p1 x then 0 else if p2 x then 1 else 2+  let (as', is) = intrinsics.partition (3, p', as)+  in (as'[0:is[0]], as'[is[0]:is[0]+is[1]], as'[is[0]+is[1]:n])++-- | `reduce_stream op f as` splits `as` into chunks, applies `f` to each+-- of these in parallel, and uses `op` (which must be associative) to+-- combine the per-chunk results into a final result.  The `i32`+-- passed to `f` is the size of the chunk.  This SOAC is useful when+-- `f` can be given a particularly work-efficient sequential+-- implementation.  Operationally, we can imagine that `as` is divided+-- among as many threads as necessary to saturate the machine, with+-- each thread operating sequentially.+--+-- A chunk may be empty, and `f 0 []` must produce the neutral element for+-- `op`.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let reduce_stream [n] 'a 'b (op: b -> b -> b) (f: (k: i32) -> [k]a -> b) (as: [n]a): b =+  intrinsics.reduce_stream (op, f, as)++-- | As `reduce_stream`@term, but the chunks do not necessarily+-- correspond to subsequences of the original array (they may be+-- interleaved).+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let reduce_stream_per [n] 'a 'b (op: b -> b -> b) (f: (k: i32) -> [k]a -> b) (as: [n]a): b =+  intrinsics.reduce_stream_per (op, f, as)++-- | Similar to `reduce_stream`@term, except that each chunk must produce+-- an array *of the same size*.  The per-chunk results are+-- concatenated.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let map_stream [n] 'a 'b (f: (k: i32) -> [k]a -> [k]b) (as: [n]a): *[n]b =+  intrinsics.map_stream (f, as) :> *[n]b++-- | Similar to `map_stream`@term, but the chunks do not necessarily+-- correspond to subsequences of the original array (they may be+-- interleaved).+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let map_stream_per [n] 'a 'b (f: (k: i32) -> [k]a -> [k]b) (as: [n]a): *[n]b =+  intrinsics.map_stream_per (f, as) :> *[n]b++-- | Return `true` if the given function returns `true` for all+-- elements in the array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let all [n] 'a (f: a -> bool) (as: [n]a): bool =+  reduce (&&) true (map f as)++-- | Return `true` if the given function returns `true` for any+-- elements in the array.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(log(n))*+let any [n] 'a (f: a -> bool) (as: [n]a): bool =+  reduce (||) false (map f as)++-- | The `scatter as is vs` expression calculates the equivalent of+-- this imperative code:+--+-- ```+-- for index in 0..length is-1:+--   i = is[index]+--   v = vs[index]+--   as[i] = v+-- ```+--+-- The `is` and `vs` arrays must have the same outer size.  `scatter`+-- acts in-place and consumes the `as` array, returning a new array+-- that has the same type and elements as `as`, except for the indices+-- in `is`.  If `is` contains duplicates (i.e. several writes are+-- performed to the same location), the result is unspecified.  It is+-- not guaranteed that one of the duplicate writes will complete+-- atomically - they may be interleaved.  See `reduce_by_index`@term+-- for a function that can handle this case deterministically.+--+-- This is technically not a second-order operation, but it is defined+-- here because it is closely related to the SOACs.+--+-- **Work:** *O(n)*+--+-- **Span:** *O(1)*+let scatter 't [m] [n] (dest: *[m]t) (is: [n]i32) (vs: [n]t): *[m]t =+  intrinsics.scatter (dest, is, vs) :> *[m]t
+ prelude/zip.fut view
@@ -0,0 +1,58 @@+-- | Transforming arrays of tuples into tuples of arrays and back+-- again.  These are generally very cheap operations, as the internal+-- compiler representation is always tuples of arrays.++-- The main reason this module exists is that we need it to define+-- SOACs like `map2`.++-- We need a map to define some of the zip variants, but this file is+-- depended upon by soacs.fut.  So we just define a quick-and-dirty+-- internal one here that uses the intrinsic version.+local let internal_map 'a [n] 'x (f: a -> x) (as: [n]a): [n]x =+  intrinsics.map (f, as) :> [n]x++-- | Construct an array of pairs from two arrays.+let zip [n] 'a 'b (as: [n]a) (bs: [n]b): [n](a,b) =+  intrinsics.zip (as, bs) :> [n](a,b)++-- | Construct an array of pairs from two arrays.+let zip2 [n] 'a 'b (as: [n]a) (bs: [n]b): [n](a,b) =+  zip as bs :> [n](a,b)++-- | As `zip2`@term, but with one more array.+let zip3 [n] 'a 'b 'c (as: [n]a) (bs: [n]b) (cs: [n]c): [n](a,b,c) =+  internal_map (\(a,(b,c)) -> (a,b,c)) (zip as (zip2 bs cs))++-- | As `zip3`@term, but with one more array.+let zip4 [n] 'a 'b 'c 'd (as: [n]a) (bs: [n]b) (cs: [n]c) (ds: [n]d): [n](a,b,c,d) =+  internal_map (\(a,(b,c,d)) -> (a,b,c,d)) (zip as (zip3 bs cs ds))++-- | As `zip4`@term, but with one more array.+let zip5 [n] 'a 'b 'c 'd 'e (as: [n]a) (bs: [n]b) (cs: [n]c) (ds: [n]d) (es: [n]e): [n](a,b,c,d,e) =+  internal_map (\(a,(b,c,d,e)) -> (a,b,c,d,e)) (zip as (zip4 bs cs ds es))++-- | Turn an array of pairs into two arrays.+let unzip [n] 'a 'b (xs: [n](a,b)): ([n]a, [n]b) =+  intrinsics.unzip xs :> ([n]a, [n]b)++-- | Turn an array of pairs into two arrays.+let unzip2 [n] 'a 'b (xs: [n](a,b)): ([n]a, [n]b) =+  unzip xs++-- | As `unzip2`@term, but with one more array.+let unzip3 [n] 'a 'b 'c (xs: [n](a,b,c)): ([n]a, [n]b, [n]c) =+  let (as, bcs) = unzip (internal_map (\(a,b,c) -> (a,(b,c))) xs)+  let (bs, cs) = unzip bcs+  in (as, bs, cs)++-- | As `unzip3`@term, but with one more array.+let unzip4 [n] 'a 'b 'c 'd (xs: [n](a,b,c,d)): ([n]a, [n]b, [n]c, [n]d) =+  let (as, bs, cds) = unzip3 (internal_map (\(a,b,c,d) -> (a,b,(c,d))) xs)+  let (cs, ds) = unzip cds+  in (as, bs, cs, ds)++-- | As `unzip4`@term, but with one more array.+let unzip5 [n] 'a 'b 'c 'd 'e (xs: [n](a,b,c,d,e)): ([n]a, [n]b, [n]c, [n]d, [n]e) =+  let (as, bs, cs, des) = unzip4 (internal_map (\(a,b,c,d,e) -> (a,b,c,(d,e))) xs)+  let (ds, es) = unzip des+  in (as, bs, cs, ds, es)
rts/c/cuda.h view
@@ -67,7 +67,7 @@   cfg->load_ptx_from = NULL;    cfg->default_block_size = 256;-  cfg->default_grid_size = 128;+  cfg->default_grid_size = 256;   cfg->default_tile_size = 32;   cfg->default_threshold = 32*1024; 
rts/c/opencl.h view
@@ -4,13 +4,13 @@ #define OPENCL_SUCCEED_NONFATAL(e) opencl_succeed_nonfatal(e, #e, __FILE__, __LINE__) // Take care not to override an existing error. #define OPENCL_SUCCEED_OR_RETURN(e) {             \-    char *error = OPENCL_SUCCEED_NONFATAL(e);     \-    if (error) {                                  \+    char *serror = OPENCL_SUCCEED_NONFATAL(e);    \+    if (serror) {                                 \       if (!ctx->error) {                          \-        ctx->error = error;                       \+        ctx->error = serror;                      \         return bad;                               \       } else {                                    \-        free(error);                              \+        free(serror);                             \       }                                           \     }                                             \   }
rts/csharp/scalar.cs view
@@ -281,6 +281,12 @@ private static double futhark_acos64(double x){return Math.Acos(x);} private static double futhark_asin64(double x){return Math.Asin(x);} private static double futhark_atan64(double x){return Math.Atan(x);}+private static double futhark_cosh64(double x){return Math.Cosh(x);}+private static double futhark_sinh64(double x){return Math.Sinh(x);}+private static double futhark_tanh64(double x){return Math.Tanh(x);}+private static double futhark_acosh64(double x){return Math.Acosh(x);}+private static double futhark_asinh64(double x){return Math.Asinh(x);}+private static double futhark_atanh64(double x){return Math.Atanh(x);} private static double futhark_atan2_64(double x, double y){return Math.Atan2(x, y);} private static double futhark_gamma64(double x){throw new NotImplementedException();} private static double futhark_lgamma64(double x){throw new NotImplementedException();}@@ -300,6 +306,12 @@ private static float futhark_acos32(float x){return (float) Math.Acos(x);} private static float futhark_asin32(float x){return (float) Math.Asin(x);} private static float futhark_atan32(float x){return (float) Math.Atan(x);}+private static float futhark_cosh32(float x){return (float) Math.Cosh(x);}+private static float futhark_sinh32(float x){return (float) Math.Sinh(x);}+private static float futhark_tanh32(float x){return (float) Math.Tanh(x);}+private static float futhark_acosh32(float x){return (float) Math.Acosh(x);}+private static float futhark_asinh32(float x){return (float) Math.Asinh(x);}+private static float futhark_atanh32(float x){return (float) Math.Atanh(x);} private static float futhark_atan2_32(float x, float y){return (float) Math.Atan2(x, y);} private static float futhark_gamma32(float x){throw new NotImplementedException();} private static float futhark_lgamma32(float x){throw new NotImplementedException();}@@ -318,6 +330,13 @@ private static float futhark_lerp32(float v0, float v1, float t){return v0 + (v1-v0)*t;} private static double futhark_lerp64(double v0, double v1, double t){return v0 + (v1-v0)*t;} +private static float futhark_fma32(float a, float b, float c){return a*b+c;}+private static double futhark_fma64(double a, double b, double c){return a*b+c;}++private static float futhark_mad32(float a, float b, float c){return a*b+c;}+private static double futhark_mad64(double a, double b, double c){return a*b+c;}++ int futhark_popc8 (sbyte x) {   int c = 0;   for (; x != 0; ++c) {@@ -392,6 +411,57 @@         x <<= 1;     }     return n;+}++sbyte futhark_mul_hi8(sbyte a, sbyte b) {+    ushort aa = (ushort)(byte)a;+    ushort bb = (ushort)(byte)b;+    return (sbyte)((aa * bb) >> 8);+}++short futhark_mul_hi16(short a, short b) {+    uint aa = (uint)(ushort)a;+    uint bb = (uint)(ushort)b;+    return (short)((aa * bb) >> 16);+}++int futhark_mul_hi32(int a, int b) {+    ulong aa = (ulong)(uint)a;+    ulong bb = (ulong)(uint)b;+    return (int)((aa * bb) >> 32);+}++// By Ben Voigt at+// https://stackoverflow.com/questions/29722093/computing-the-high-bits-of-a-multiplication-in-c-sharp+long futhark_mul_hi64(long xx, long yy) {+    ulong x = (ulong)xx;+    ulong y = (ulong)yy;+    ulong accum = ((ulong)(uint)x) * ((ulong)(uint)y);+    accum >>= 32;+    ulong term1 = (x >> 32) * ((ulong)(uint)y);+    ulong term2 = (y >> 32) * ((ulong)(uint)x);+    accum += (uint)term1;+    accum += (uint)term2;+    accum >>= 32;+    accum += (term1 >> 32) + (term2 >> 32);+    accum += (x >> 32) * (y >> 32);+    return (long)accum;+}++sbyte futhark_mad_hi8(sbyte a, sbyte b, sbyte c) {+    return (sbyte)(futhark_mul_hi8(a,b) + c);+}++short futhark_mad_hi16(short a, short b, short c) {+    return (short)(futhark_mul_hi16(a,b) + c);+}++int futhark_mad_hi32(int a, int b, int c) {+    return futhark_mul_hi32(a,b) + c;+}++long futhark_mad_hi64(long a, long b, long c) {+    return futhark_mul_hi64(a,b) + c; }  private static bool llt (bool x, bool y){return (!x && y);}
rts/python/opencl.py view
@@ -112,6 +112,12 @@     self.max_local_memory = int(self.device.local_mem_size)     self.free_list = {} +    self.global_failure = self.pool.allocate(np.int32().itemsize)+    cl.enqueue_fill_buffer(self.queue, self.global_failure, np.int32(-1), 0, np.int32().itemsize)+    self.global_failure_args = self.pool.allocate(np.int32().itemsize *+                                                  (self.global_failure_args_max+1))+    self.failure_is_an_option = np.int32(0)+     if 'default_group_size' in sizes:         default_group_size = sizes['default_group_size']         del sizes['default_group_size']@@ -201,3 +207,12 @@  def opencl_free_all(self):     self.pool.free_held()++def sync(self):+    failure = np.empty(1, dtype=np.int32)+    cl.enqueue_copy(self.queue, failure, self.global_failure, is_blocking=True)+    self.failure_is_an_option = np.int32(0)+    if failure[0] >= 0:+        failure_args = np.empty(self.global_failure_args_max+1, dtype=np.int32)+        cl.enqueue_copy(self.queue, failure_args, self.global_failure_args, is_blocking=True)+        raise Exception(self.failure_msgs[failure[0]].format(*failure_args))
rts/python/scalar.py view
@@ -280,6 +280,39 @@ def fpconv_f64_f32(x):   return np.float32(x) +def futhark_mul_hi8(a, b):+  a = np.uint64(np.uint8(a))+  b = np.uint64(np.uint8(b))+  return np.int8((a*b) >> np.uint64(8))++def futhark_mul_hi16(a, b):+  a = np.uint64(np.uint16(a))+  b = np.uint64(np.uint16(b))+  return np.int16((a*b) >> np.uint64(16))++def futhark_mul_hi32(a, b):+  a = np.uint64(np.uint32(a))+  b = np.uint64(np.uint32(b))+  return np.int32((a*b) >> np.uint64(32))++# This one is done with arbitrary-precision integers.+def futhark_mul_hi64(a, b):+  a = int(np.uint64(a))+  b = int(np.uint64(b))+  return np.int64(np.uint64(a*b >> 64))++def futhark_mad_hi8(a, b, c):+  return futhark_mul_hi8(a,b) + c++def futhark_mad_hi16(a, b, c):+  return futhark_mul_hi16(a,b) + c++def futhark_mad_hi32(a, b, c):+  return futhark_mul_hi32(a,b) + c++def futhark_mad_hi64(a, b, c):+  return futhark_mul_hi64(a,b) + c+ def futhark_log64(x):   return np.float64(np.log(x)) @@ -313,6 +346,24 @@ def futhark_atan64(x):   return np.arctan(x) +def futhark_cosh64(x):+  return np.cosh(x)++def futhark_sinh64(x):+  return np.sinh(x)++def futhark_tanh64(x):+  return np.tanh(x)++def futhark_acosh64(x):+  return np.arccosh(x)++def futhark_asinh64(x):+  return np.arcsinh(x)++def futhark_atanh64(x):+  return np.arctanh(x)+ def futhark_atan2_64(x, y):   return np.arctan2(x, y) @@ -378,6 +429,24 @@ def futhark_atan32(x):   return np.arctan(x) +def futhark_cosh32(x):+  return np.cosh(x)++def futhark_sinh32(x):+  return np.sinh(x)++def futhark_tanh32(x):+  return np.tanh(x)++def futhark_acosh32(x):+  return np.arccosh(x)++def futhark_asinh32(x):+  return np.arcsinh(x)++def futhark_atanh32(x):+  return np.arctanh(x)+ def futhark_atan2_32(x, y):   return np.arctan2(x, y) @@ -415,5 +484,17 @@  def futhark_lerp64(v0, v1, t):   return v0 + (v1-v0)*t++def futhark_mad32(a, b, c):+  return a * b + c++def futhark_mad64(a, b, c):+  return a * b + c++def futhark_fma32(a, b, c):+  return a * b + c++def futhark_fma64(a, b, c):+  return a * b + c  # End of scalar.py.
src/Futhark/Analysis/Alias.hs view
@@ -28,7 +28,7 @@ -- | Perform alias analysis on a Futhark program. aliasAnalysis :: (Attributes lore, CanBeAliased (Op lore)) =>                  Prog lore -> Prog (Aliases lore)-aliasAnalysis = Prog . map analyseFun . progFunctions+aliasAnalysis = Prog . map analyseFun . progFuns  analyseFun :: (Attributes lore, CanBeAliased (Op lore)) =>               FunDef lore -> FunDef (Aliases lore)
src/Futhark/Analysis/CallGraph.hs view
@@ -17,7 +17,7 @@ type FunctionTable = M.Map Name (FunDef SOACS)  buildFunctionTable :: Prog SOACS -> FunctionTable-buildFunctionTable = foldl expand M.empty . progFunctions+buildFunctionTable = foldl expand M.empty . progFuns   where expand ftab f = M.insert (funDefName f) f ftab  -- | The call graph is just a mapping from a function name, i.e., the@@ -29,7 +29,7 @@ buildCallGraph :: Prog SOACS -> CallGraph buildCallGraph prog = foldl' (buildCGfun ftable) M.empty entry_points   where entry_points = map funDefName $ filter (isJust . funDefEntryPoint) $-                       progFunctions prog+                       progFuns prog         ftable = buildFunctionTable prog  -- | @buildCallGraph ftable cg fname@ updates @cg@ with the
src/Futhark/Analysis/HORepresentation/SOAC.hs view
@@ -431,7 +431,7 @@                   | t <- drop (length nes) (lambdaReturnType lam) ]   in  accrtps ++ arrtps typeOf (Scatter _w lam _ivs dests) =-  zipWith arrayOfRow (snd $ splitAt (n `div` 2) lam_ts) aws+  zipWith arrayOfRow (drop (n `div` 2) lam_ts) aws   where lam_ts = lambdaReturnType lam         n = length lam_ts         (aws, _, _) = unzip3 dests@@ -634,9 +634,7 @@     where mkMapPlusAccLam :: (MonadFreshNames m, Bindable lore)                           => [SubExp] -> Lambda lore -> m (Lambda lore)           mkMapPlusAccLam accs plus = do-            let lampars = lambdaParams plus-                (accpars, rempars) = (  take (length accs) lampars,-                                        drop (length accs) lampars  )+            let (accpars, rempars) = splitAt (length accs) $ lambdaParams plus                 parbnds = zipWith (\ par se -> mkLet [] [paramIdent par]                                                         (BasicOp $ SubExp se)                                   ) accpars accs
src/Futhark/Analysis/Metrics.hs view
@@ -73,7 +73,7 @@         addWhat' (ctx, k) = (what : ctx, k)  progMetrics :: OpMetrics (Op lore) => Prog lore -> AstMetrics-progMetrics = actualMetrics . execWriter . runMetricsM . mapM_ funDefMetrics . progFunctions+progMetrics = actualMetrics . execWriter . runMetricsM . mapM_ funDefMetrics . progFuns  funDefMetrics :: OpMetrics (Op lore) => FunDef lore -> MetricsM () funDefMetrics = bodyMetrics . funDefBody
src/Futhark/Analysis/PrimExp.hs view
@@ -7,6 +7,7 @@   , primExpType   , primExpSizeAtLeast   , coerceIntPrimExp+  , leafExpTypes   , true   , false   , constFoldPrimExp@@ -18,6 +19,7 @@ import           Control.Monad import           Data.Traversable import qualified Data.Map as M+import qualified Data.Set as S  import           Futhark.Representation.AST.Attributes.Names import           Futhark.Representation.Primitive@@ -345,3 +347,15 @@   ppr (ConvOpExp op x)  = ppr op <+> parens (ppr x)   ppr (UnOpExp op x)    = ppr op <+> parens (ppr x)   ppr (FunExp h args _) = text h <+> parens (commasep $ map ppr args)++leafExpTypes :: Ord a => PrimExp a -> S.Set (a, PrimType)+leafExpTypes (LeafExp x ptp) = S.singleton (x, ptp)+leafExpTypes (ValueExp _) = S.empty+leafExpTypes (UnOpExp _ e) = leafExpTypes e+leafExpTypes (ConvOpExp _ e) = leafExpTypes e+leafExpTypes (BinOpExp _ e1 e2) =+  S.union (leafExpTypes e1) (leafExpTypes e2)+leafExpTypes (CmpOpExp _ e1 e2) =+  S.union (leafExpTypes e1) (leafExpTypes e2)+leafExpTypes (FunExp _ pes _) =+  S.unions $ map leafExpTypes pes
src/Futhark/Analysis/PrimExp/Convert.hs view
@@ -4,6 +4,7 @@   (     primExpToExp   , primExpFromExp+  , primExpToSubExp   , primExpFromSubExp   , primExpFromSubExpM   , replaceInPrimExp@@ -76,10 +77,9 @@       FunExp (nameToString fname) <$> mapM (primExpFromSubExpM f . fst) args <*> pure t primExpFromExp _ _ = fail "Not a PrimExp" -primExpFromSubExpM :: Fail.MonadFail m =>-                      (VName -> m (PrimExp v)) -> SubExp -> m (PrimExp v)+primExpFromSubExpM :: Applicative m => (VName -> m (PrimExp v)) -> SubExp -> m (PrimExp v) primExpFromSubExpM f (Var v) = f v-primExpFromSubExpM _ (Constant v) = return $ ValueExp v+primExpFromSubExpM _ (Constant v) = pure $ ValueExp v  -- | Convert 'SubExp's of a given type. primExpFromSubExp :: PrimType -> SubExp -> PrimExp VName
+ src/Futhark/Analysis/PrimExp/Generalize.hs view
@@ -0,0 +1,70 @@+module Futhark.Analysis.PrimExp.Generalize+  (+    leastGeneralGeneralization+  ) where++import           Control.Monad+import           Data.List (elemIndex)+++import           Futhark.Analysis.PrimExp+import           Futhark.Representation.AST.Syntax.Core (Ext(..))++-- | Generalization (anti-unification)+-- We assume that the two expressions have the same type.+leastGeneralGeneralization :: (Eq v) => [(PrimExp v, PrimExp v)] -> PrimExp v -> PrimExp v ->+                              Maybe (PrimExp (Ext v), [(PrimExp v, PrimExp v)])+leastGeneralGeneralization m exp1@(LeafExp v1 t1) exp2@(LeafExp v2 _) =+  if v1 == v2 then+    Just (LeafExp (Free v1) t1, m)+  else+    Just $ generalize m exp1 exp2+leastGeneralGeneralization m exp1@(ValueExp v1) exp2@(ValueExp v2) =+  if v1 == v2 then+    Just (ValueExp v1, m)+  else+    Just $ generalize m exp1 exp2+leastGeneralGeneralization m exp1@(BinOpExp op1 e11 e12) exp2@(BinOpExp op2 e21 e22) =+  if op1 == op2 then do+    (e1, m1) <- leastGeneralGeneralization m e11 e21+    (e2, m2) <- leastGeneralGeneralization m1 e12 e22+    return (BinOpExp op1 e1 e2, m2)+  else+    Just $ generalize m exp1 exp2+leastGeneralGeneralization m exp1@(CmpOpExp op1 e11 e12) exp2@(CmpOpExp op2 e21 e22) =+  if op1 == op2 then do+    (e1, m1) <- leastGeneralGeneralization m e11 e21+    (e2, m2) <- leastGeneralGeneralization m1 e12 e22+    return (CmpOpExp op1 e1 e2, m2)+  else+    Just $ generalize m exp1 exp2+leastGeneralGeneralization m exp1@(UnOpExp op1 e1) exp2@(UnOpExp op2 e2) =+  if op1 == op2 then do+    (e, m1) <- leastGeneralGeneralization m e1 e2+    return (UnOpExp op1 e, m1)+  else+    Just $ generalize m exp1 exp2+leastGeneralGeneralization m exp1@(ConvOpExp op1 e1) exp2@(ConvOpExp op2 e2) =+  if op1 == op2 then do+    (e, m1) <- leastGeneralGeneralization m e1 e2+    return (ConvOpExp op1 e, m1)+  else+    Just $ generalize m exp1 exp2+leastGeneralGeneralization m exp1@(FunExp s1 args1 t1) exp2@(FunExp s2 args2 _) =+  if s1 == s2 && length args1 == length args2 then do+    (args, m') <- foldM (\(arg_acc, m_acc) (a1, a2) -> do+                            (a, m'') <- leastGeneralGeneralization m_acc a1 a2+                            return (a : arg_acc, m'')+                        ) ([], m) (zip args1 args2)+    return (FunExp s1 (reverse args) t1, m')+  else+    Just $ generalize m exp1 exp2+leastGeneralGeneralization m exp1 exp2 =+  Just $ generalize m exp1 exp2++generalize :: Eq v => [(PrimExp v, PrimExp v)] -> PrimExp v -> PrimExp v -> (PrimExp (Ext v), [(PrimExp v, PrimExp v)])+generalize m exp1 exp2 =+  let t = primExpType exp1+  in case elemIndex (exp1, exp2) m of+       Just i -> (LeafExp (Ext i) t, m)+       Nothing -> (LeafExp (Ext $ length m) t, m ++ [(exp1, exp2)])
src/Futhark/Analysis/Range.hs view
@@ -25,7 +25,7 @@ -- program with embedded range annotations. rangeAnalysis :: (Attributes lore, CanBeRanged (Op lore)) =>                  Prog lore -> Prog (Ranges lore)-rangeAnalysis = Prog . map analyseFun . progFunctions+rangeAnalysis = Prog . map analyseFun . progFuns  -- Implementation 
src/Futhark/Analysis/Rephrase.hs view
@@ -30,7 +30,7 @@               }  rephraseProg :: Monad m => Rephraser m from to -> Prog from -> m (Prog to)-rephraseProg rephraser = fmap Prog . mapM (rephraseFunDef rephraser) . progFunctions+rephraseProg rephraser = fmap Prog . mapM (rephraseFunDef rephraser) . progFuns  rephraseFunDef :: Monad m => Rephraser m from to -> FunDef from -> m (FunDef to) rephraseFunDef rephraser fundec = do
src/Futhark/Analysis/SymbolTable.hs view
@@ -411,7 +411,7 @@ enclosingLoopVars :: [VName] -> SymbolTable lore -> [VName] enclosingLoopVars free vtable =   map fst $-  sortBy (flip (comparing (bindingDepth . snd))) $+  sortOn (Down . bindingDepth . snd) $   filter (loopVariable . snd) $ mapMaybe fetch free   where fetch name = do e <- lookup name vtable                         return (name, e)
src/Futhark/Analysis/Usage.hs view
@@ -1,8 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} module Futhark.Analysis.Usage ( usageInStm ) where -import Data.Foldable- import Futhark.Representation.AST import Futhark.Representation.AST.Attributes.Aliases import qualified Futhark.Analysis.UsageTable as UT@@ -30,7 +28,7 @@             namesToList $ subExpAliases se           | (v,se) <- merge, unique $ paramDeclType v ] usageInExp (If _ tbranch fbranch _) =-  fold $ map UT.consumedUsage $ namesToList $+  foldMap UT.consumedUsage $ namesToList $   consumedInBody tbranch <> consumedInBody fbranch usageInExp (BasicOp (Update src _ _)) =   UT.consumedUsage src
src/Futhark/Bench.hs view
@@ -215,6 +215,7 @@   CompileOptions   { compFuthark :: String   , compBackend :: String+  , compOptions :: [String]   }  progNotFound :: String -> String@@ -239,7 +240,9 @@      Right () -> do       (futcode, _, futerr) <- liftIO $ readProcessWithExitCode futhark-                              [compBackend opts, program, "-o", binaryName program] ""+                              ([compBackend opts, program, "-o", binaryName program] <>+                               compOptions opts)+                              ""        case futcode of         ExitSuccess     -> return $ Right ()
src/Futhark/Binder.hs view
@@ -31,7 +31,6 @@ import Control.Monad.Reader import Control.Monad.Error.Class import qualified Data.Map.Strict as M-import qualified Control.Monad.Fail as Fail  import Futhark.Binder.Class import Futhark.Representation.AST@@ -62,9 +61,6 @@ instance MonadTrans (BinderT lore) where   lift = BinderT . lift -instance Monad m => Fail.MonadFail (BinderT lore m) where-  fail = error . ("BinderT.fail: "++)- type Binder lore = BinderT lore (State VNameSource)  instance MonadFreshNames m => MonadFreshNames (BinderT lore m) where@@ -76,7 +72,7 @@   lookupType name = do     t <- BinderT $ gets $ M.lookup name . snd     case t of-      Nothing -> fail $ "BinderT.lookupType: unknown variable " ++ pretty name+      Nothing -> error $ "BinderT.lookupType: unknown variable " ++ pretty name       Just t' -> return $ typeOf t'   askScope = BinderT $ gets snd 
src/Futhark/Binder/Class.hs view
@@ -21,7 +21,6 @@ where  import Control.Monad.Writer-import qualified Control.Monad.Fail as Fail import qualified Data.Kind  import Futhark.Representation.AST@@ -57,8 +56,7 @@ -- bindings, however. class (Attributes (Lore m),        MonadFreshNames m, Applicative m, Monad m,-       LocalScope (Lore m) m,-       Fail.MonadFail m) =>+       LocalScope (Lore m) m) =>       MonadBinder m where   type Lore m :: Data.Kind.Type   mkExpAttrM :: Pattern (Lore m) -> Exp (Lore m) -> m (ExpAttr (Lore m))
src/Futhark/CLI/Autotune.hs view
@@ -39,7 +39,9 @@ compileOptions opts = do   futhark <- maybe getExecutablePath return $ optFuthark opts   return $ CompileOptions { compFuthark = futhark-                          , compBackend = optBackend opts }+                          , compBackend = optBackend opts+                          , compOptions = mempty+                          }  runOptions :: Path -> Int -> AutotuneOptions -> RunOptions runOptions path timeout_s opts =
src/Futhark/CLI/Bench.hs view
@@ -12,6 +12,7 @@ import Data.Either import Data.Maybe import Data.List+import Data.Ord import qualified Data.Text as T import System.Console.GetOpt import System.Directory@@ -32,6 +33,7 @@                    , optRunner :: String                    , optRuns :: Int                    , optExtraOptions :: [String]+                   , optCompilerOptions :: [String]                    , optJSON :: Maybe FilePath                    , optTimeout :: Int                    , optSkipCompilation :: Bool@@ -43,7 +45,7 @@                    }  initialBenchOptions :: BenchOptions-initialBenchOptions = BenchOptions "c" Nothing "" 10 [] Nothing (-1) False+initialBenchOptions = BenchOptions "c" Nothing "" 10 [] [] Nothing (-1) False                       ["nobench", "disable"] [] Nothing (Just "tuning") Nothing  runBenchmarks :: BenchOptions -> [FilePath] -> IO ()@@ -63,7 +65,8 @@    when (anyFailedToCompile skipped_benchmarks) exitFailure -  results <- concat <$> mapM (runBenchmark opts) compiled_benchmarks+  results <- concat <$> mapM (runBenchmark opts)+             (sortBy (comparing fst) compiled_benchmarks)   case optJSON opts of     Nothing -> return ()     Just file -> LBS.writeFile file $ encodeBenchResults results@@ -88,7 +91,9 @@ compileOptions opts = do   futhark <- maybe getExecutablePath return $ optFuthark opts   return $ CompileOptions { compFuthark = futhark-                          , compBackend = optBackend opts }+                          , compBackend = optBackend opts+                          , compOptions = optCompilerOptions opts+                          }  compileBenchmark :: BenchOptions -> (FilePath, ProgramTest)                  -> IO (Either SkipReason (FilePath, [InputOutputs]))@@ -213,6 +218,12 @@                config { optExtraOptions = opt : optExtraOptions config })      "OPT")     "Pass this option to programs being run."+  , Option [] ["pass-compiler-option"]+    (ReqArg (\opt ->+               Right $ \config ->+               config { optCompilerOptions = opt : optCompilerOptions config })+     "OPT")+    "Pass this option to the compiler (or typechecker if in -t mode)."   , Option [] ["json"]     (ReqArg (\file ->                Right $ \config -> config { optJSON = Just file})
+ src/Futhark/CLI/Check.hs view
@@ -0,0 +1,28 @@+module Futhark.CLI.Check (main) where++import Control.Monad+import Control.Monad.IO.Class+import System.Console.GetOpt+import System.IO++import Futhark.Compiler+import Futhark.Util.Options++newtype CheckConfig = CheckConfig { checkWarn :: Bool }++newCheckConfig :: CheckConfig+newCheckConfig = CheckConfig True++options :: [FunOptDescr CheckConfig]+options = [Option "w" []+           (NoArg $ Right $ \cfg -> cfg { checkWarn = False })+           "Disable all warnings."]++main :: String -> [String] -> IO ()+main = mainWithOptions newCheckConfig options "program" $ \args cfg ->+  case args of+    [file] -> Just $ do+      (warnings, _, _) <- readProgramOrDie file+      when (checkWarn cfg) $+        liftIO $ hPutStr stderr $ show warnings+    _ -> Nothing
src/Futhark/CLI/Dataset.hs view
@@ -13,8 +13,9 @@ import qualified Data.Vector.Unboxed.Mutable as UMVec import qualified Data.Vector.Unboxed as UVec import Data.Vector.Generic (freeze)- import System.Console.GetOpt+import System.Exit+import System.IO import System.Random  import Language.Futhark.Syntax hiding@@ -32,7 +33,8 @@           | null $ optOrders config = Just $ do               maybe_vs <- readValues <$> BS.getContents               case maybe_vs of-                Nothing -> error "Malformed data on standard input."+                Nothing -> do hPutStrLn stderr "Malformed data on standard input."+                              exitFailure                 Just vs ->                   case format config of                     Text -> mapM_ (putStrLn . pretty) vs@@ -66,7 +68,8 @@                 [(n', "")] ->                   Right $ \config -> config { optSeed = n' }                 _ ->-                  Left $ error $ "'" ++ n ++ "' is not an integer.")+                  Left $ do hPutStrLn stderr $ "'" ++ n ++ "' is not an integer."+                            exitFailure)      "SEED")     "The seed to use when initialising the RNG."   , Option "g" ["generate"]@@ -79,7 +82,8 @@                              [g (optRange config) (format config)]                          }                 Left err ->-                  Left $ error err)+                  Left $ do hPutStrLn stderr err+                            exitFailure)      "TYPE")     "Generate a random value of this type."   , Option [] ["text"]@@ -116,7 +120,9 @@                 Right $ \config ->                 config { optRange = set (lower', upper') $ optRange config }               _ ->-                Left $ error $ "Invalid bounds: " ++ b+                Left $ do+                hPutStrLn stderr $ "Invalid bounds for " ++ tname ++ ": " ++ b+                exitFailure             )    "MIN:MAX") $   "Range of " ++ tname ++ " values."
src/Futhark/CLI/Dev.hs view
@@ -3,6 +3,7 @@ module Futhark.CLI.Dev (main) where  import Data.Maybe+import Data.List import Control.Category (id) import Control.Monad import Control.Monad.State@@ -292,6 +293,9 @@   , Option [] ["ast"]     (NoArg $ Right $ \opts -> opts { futharkPrintAST = True })     "Output ASTs instead of prettyprinted programs."+  , Option [] ["safe"]+    (NoArg $ Right $ changeFutharkConfig $ \opts -> opts { futharkSafe = True })+    "Ignore 'unsafe'."   , typedPassOption soacsProg Kernels firstOrderTransform "f"   , soacsPassOption fuseSOACs "o"   , soacsPassOption inlineAndRemoveDeadFunctions []@@ -343,7 +347,12 @@               Right () -> return ()         compile _      _      =           Nothing-        m file config =+        m file config = do+          let p :: (Show a, PP.Pretty a) => [a] -> IO ()+              p = mapM_ putStrLn .+                  intersperse "" .+                  map (if futharkPrintAST config then show else pretty)+           case futharkPipeline config of             PrettyPrint -> liftIO $ do               maybe_prog <- parseFuthark file <$> T.readFile file@@ -359,17 +368,16 @@                            else pretty $ fileProg fm             Defunctorise -> do               (_, imports, src) <- readProgram file-              liftIO $ mapM_ (putStrLn . if futharkPrintAST config then show else pretty) $-                evalState (Defunctorise.transformProg imports) src+              liftIO $ p $ evalState (Defunctorise.transformProg imports) src             Monomorphise -> do               (_, imports, src) <- readProgram file-              liftIO $ mapM_ (putStrLn . if futharkPrintAST config then show else pretty) $+              liftIO $ p $                 flip evalState src $                 Defunctorise.transformProg imports                 >>= Monomorphise.transformProg             Defunctionalise -> do               (_, imports, src) <- readProgram file-              liftIO $ mapM_ (putStrLn . if futharkPrintAST config then show else pretty) $+              liftIO $ p $                 flip evalState src $                 Defunctorise.transformProg imports                 >>= Monomorphise.transformProg
src/Futhark/CLI/Misc.hs view
@@ -1,8 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} -- Various small subcommands that are too simple to deserve their own file. module Futhark.CLI.Misc-  ( mainCheck-  , mainImports+  ( mainImports   , mainDataget   ) where@@ -19,21 +18,13 @@ import Futhark.Util.Options import Futhark.Test -mainCheck :: String -> [String] -> IO ()-mainCheck = mainWithOptions () [] "program" $ \args () ->-  case args of-    [file] -> Just $ do-      (warnings, _, _) <- readProgramOrDie file-      liftIO $ hPutStr stderr $ show warnings-    _ -> Nothing- mainImports :: String -> [String] -> IO () mainImports = mainWithOptions () [] "program" $ \args () ->   case args of     [file] -> Just $ do       (_, prog_imports, _) <- readProgramOrDie file       liftIO $ putStr $ unlines $ map (++ ".fut")-        $ filter (\f -> not ("futlib/" `isPrefixOf` f))+        $ filter (\f -> not ("prelude/" `isPrefixOf` f))         $ map fst prog_imports     _ -> Nothing 
src/Futhark/CLI/REPL.hs view
@@ -140,34 +140,35 @@      Nothing -> do       -- Load the builtins through the type checker.-      (_, imports, src) <- badOnLeft =<< runExceptT (readLibrary [])+      (_, imports, src) <- badOnLeft show =<< runExceptT (readLibrary [])       -- Then into the interpreter.-      ienv <- foldM (\ctx -> badOnLeft <=< runInterpreter' . I.interpretImport ctx)+      ienv <- foldM (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx)               I.initialCtx $ map (fmap fileProg) imports        -- Then make the prelude available in the type checker.-      (tenv, d, src') <- badOnLeft $ T.checkDec imports src T.initialEnv-                         (T.mkInitialImport ".") $ mkOpen "/futlib/prelude"+      (tenv, d, src') <- badOnLeft pretty $ T.checkDec imports src T.initialEnv+                         (T.mkInitialImport ".") $ mkOpen "/prelude/prelude"       -- Then in the interpreter.-      ienv' <- badOnLeft =<< runInterpreter' (I.interpretDec ienv d)+      ienv' <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv d)       return (imports, src', tenv, ienv')      Just file -> do       (ws, imports, src) <--        badOnLeft =<< liftIO (runExceptT (readProgram file)-                              `Haskeline.catch` \(err::IOException) ->-                                 return (Left (ExternalError (T.pack $ show err))))+        badOnLeft show =<<+        liftIO (runExceptT (readProgram file)+                 `Haskeline.catch` \(err::IOException) ->+                   return (externalErrorS (show err)))       liftIO $ hPrint stderr ws        let imp = T.mkInitialImport "."-      ienv1 <- foldM (\ctx -> badOnLeft <=< runInterpreter' . I.interpretImport ctx) I.initialCtx $+      ienv1 <- foldM (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx) I.initialCtx $                map (fmap fileProg) imports-      (tenv1, d1, src') <- badOnLeft $ T.checkDec imports src T.initialEnv imp $-                           mkOpen "/futlib/prelude"-      (tenv2, d2, src'') <- badOnLeft $ T.checkDec imports src' tenv1 imp $+      (tenv1, d1, src') <- badOnLeft pretty $ T.checkDec imports src T.initialEnv imp $+                           mkOpen "/prelude/prelude"+      (tenv2, d2, src'') <- badOnLeft pretty $ T.checkDec imports src' tenv1 imp $                             mkOpen $ toPOSIX $ dropExtension file-      ienv2 <- badOnLeft =<< runInterpreter' (I.interpretDec ienv1 d1)-      ienv3 <- badOnLeft =<< runInterpreter' (I.interpretDec ienv2 d2)+      ienv2 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv1 d1)+      ienv3 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv2 d2)       return (imports, src'', tenv2, ienv3)    return FutharkiState { futharkiImports = imports@@ -178,9 +179,9 @@                        , futharkiSkipBreaks = mempty                        , futharkiLoaded = maybe_file                        }-  where badOnLeft :: Show err => Either err a -> ExceptT String IO a-        badOnLeft (Right x) = return x-        badOnLeft (Left err) = throwError $ show err+  where badOnLeft :: (err -> String) -> Either err a -> ExceptT String IO a+        badOnLeft _ (Right x) = return x+        badOnLeft p (Left err) = throwError $ p err  getPrompt :: FutharkiM String getPrompt = do@@ -246,7 +247,7 @@   -- that 'import "foo"' is a declaration.  We have to involve a lot   -- of machinery to load this external code before executing the   -- declaration itself.-  let basis = Basis imports src ["/futlib/prelude"]+  let basis = Basis imports src ["/prelude/prelude"]       mkImport = uncurry $ T.mkImportFrom cur_import   imp_r <- runExceptT $ readImports basis (map mkImport $ decImports d) @@ -254,7 +255,7 @@     Left e -> liftIO $ print e     Right (_, imports',  src') ->       case T.checkDec imports' src' tenv cur_import d of-        Left e -> liftIO $ print e+        Left e -> liftIO $ putStrLn $ pretty e         Right (tenv', d', src'') -> do           let new_imports = filter ((`notElem` map fst imports) . fst) imports'           int_r <- runInterpreter $ do@@ -272,16 +273,21 @@ onExp :: UncheckedExp -> FutharkiM () onExp e = do   (imports, src, tenv, ienv) <- getIt-  case showErr (T.checkExp imports src tenv e) of+  case either (Left . pretty) Right $+       T.checkExp imports src tenv e of     Left err -> liftIO $ putStrLn err-    Right (_, e') -> do-      r <- runInterpreter $ I.interpretExp ienv e'-      case r of-        Left err -> liftIO $ print err-        Right v -> liftIO $ putStrLn $ pretty v-    where showErr :: Show a => Either a b -> Either String b-          showErr = either (Left . show) Right+    Right (tparams, e')+      | null tparams -> do+          r <- runInterpreter $ I.interpretExp ienv e'+          case r of+            Left err -> liftIO $ print err+            Right v -> liftIO $ putStrLn $ pretty v +      | otherwise -> liftIO $ do+          putStrLn $ "Inferred type of expression: " ++ pretty (typeOf e')+          putStrLn $ "The following types are ambiguous: " +++            intercalate ", " (map (prettyName . typeParamName) tparams)+ prettyBreaking :: Breaking -> String prettyBreaking b =   prettyStacktrace (breakingAt b) $ map locStr $ NE.toList $ breakingStack b@@ -299,7 +305,7 @@        let top = NE.head callstack           ctx = I.stackFrameCtx top-          tenv = I.typeEnv $ I.ctxEnv ctx+          tenv = I.typeCheckerEnv $ I.ctxEnv ctx           breaking = Breaking callstack 0        -- Are we supposed to skip this breakpoint?  Also, We do not@@ -346,9 +352,9 @@     (True, Nothing) -> liftIO $ T.putStrLn "No file specified and no file previously loaded."     (False, _) -> throwError $ Load $ T.unpack file -genTypeCommand :: (Show err1, Show err2) =>-                  (String -> T.Text -> Either err1 a)-               -> (Imports -> VNameSource -> T.Env -> a -> Either err2 b)+genTypeCommand :: Show err =>+                  (String -> T.Text -> Either err a)+               -> (Imports -> VNameSource -> T.Env -> a -> Either T.TypeError b)                -> (b -> String)                -> Command genTypeCommand f g h e = do@@ -360,7 +366,7 @@       src <- gets futharkiNameSource       (tenv, _) <- gets futharkiEnv       case g imports src tenv e' of-        Left err -> liftIO $ print err+        Left err -> liftIO $ putStrLn $ pretty err         Right x -> liftIO $ putStrLn $ h x  typeCommand :: Command@@ -387,7 +393,7 @@       | frame:_ <- NE.drop i stack -> do           let breaking = Breaking stack i               ctx = I.stackFrameCtx frame-              tenv = I.typeEnv $ I.ctxEnv ctx+              tenv = I.typeCheckerEnv $ I.ctxEnv ctx           modify $ \s -> s { futharkiEnv = (tenv, ctx)                            , futharkiBreaking = Just breaking                            }
src/Futhark/CLI/Run.hs view
@@ -5,7 +5,6 @@  import Control.Monad.Free.Church import Control.Exception-import Data.Bifunctor (first) import Data.List import Data.Loc import Data.Maybe@@ -13,7 +12,6 @@ import Control.Monad import Control.Monad.IO.Class import Control.Monad.Except-import qualified Data.Text as T import qualified Data.Text.IO as T import System.FilePath import System.Exit@@ -80,10 +78,8 @@  putValue :: I.Value -> TypeBase () () -> IO () putValue v t-  | I.isEmptyArray v =-      putStrLn $ "empty(" ++ pretty t' ++ ")"+  | I.isEmptyArray v = putStrLn $ I.prettyEmptyArray t v   | otherwise = putStrLn $ pretty v-  where t' = first (const 0) t `setUniqueness` Nonunique :: ValueType  data InterpreterConfig =   InterpreterConfig { interpreterEntryPoint :: Name@@ -108,25 +104,26 @@                  -> IO (Either String (T.Env, I.Ctx)) newFutharkiState cfg file = runExceptT $ do   (ws, imports, src) <--    badOnLeft =<< liftIO (runExceptT (readProgram file)-                          `Haskeline.catch` \(err::IOException) ->-                             return (Left (ExternalError (T.pack $ show err))))+    badOnLeft show =<<+    liftIO (runExceptT (readProgram file)+            `Haskeline.catch` \(err::IOException) ->+               return (externalErrorS (show err)))   when (interpreterPrintWarnings cfg) $     liftIO $ hPrint stderr ws    let imp = T.mkInitialImport "."-  ienv1 <- foldM (\ctx -> badOnLeft <=< runInterpreter' . I.interpretImport ctx) I.initialCtx $+  ienv1 <- foldM (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx) I.initialCtx $            map (fmap fileProg) imports-  (tenv1, d1, src') <- badOnLeft $ T.checkDec imports src T.initialEnv imp $-                       mkOpen "/futlib/prelude"-  (tenv2, d2, _) <- badOnLeft $ T.checkDec imports src' tenv1 imp $+  (tenv1, d1, src') <- badOnLeft pretty $ T.checkDec imports src T.initialEnv imp $+                       mkOpen "/prelude/prelude"+  (tenv2, d2, _) <- badOnLeft pretty $ T.checkDec imports src' tenv1 imp $                     mkOpen $ toPOSIX $ dropExtension file-  ienv2 <- badOnLeft =<< runInterpreter' (I.interpretDec ienv1 d1)-  ienv3 <- badOnLeft =<< runInterpreter' (I.interpretDec ienv2 d2)+  ienv2 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv1 d1)+  ienv3 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv2 d2)   return (tenv2, ienv3)-  where badOnLeft :: Show err => Either err a -> ExceptT String IO a-        badOnLeft (Right x) = return x-        badOnLeft (Left err) = throwError $ show err+  where badOnLeft :: (err -> String) -> Either err a -> ExceptT String IO a+        badOnLeft _ (Right x) = return x+        badOnLeft p (Left err) = throwError $ p err  mkOpen :: FilePath -> UncheckedDec mkOpen f = OpenDec (ModImport f NoInfo noLoc) noLoc
src/Futhark/CodeGen/Backends/CCUDA.hs view
@@ -7,8 +7,9 @@   , GC.asExecutable   ) where -import qualified Language.C.Quote.OpenCL as C+import Control.Monad import Data.List+import qualified Language.C.Quote.OpenCL as C  import qualified Futhark.CodeGen.Backends.GenericC as GC import qualified Futhark.CodeGen.ImpGen.CUDA as ImpGen@@ -27,14 +28,14 @@   res <- ImpGen.compileProg prog   case res of     Left err -> return $ Left err-    Right (Program cuda_code cuda_prelude kernel_names _ sizes prog') ->+    Right (Program cuda_code cuda_prelude kernel_names _ sizes failures prog') ->       let extra = generateBoilerplate cuda_code cuda_prelude-                                      kernel_names sizes+                                      kernel_names sizes failures       in Right <$> GC.compileProg operations extra cuda_includes                    [Space "device", DefaultSpace] cliOptions prog'   where     operations :: GC.Operations OpenCL ()-    operations = GC.Operations+    operations = GC.defaultOperations                  { GC.opsWriteScalar = writeCUDAScalar                  , GC.opsReadScalar  = readCUDAScalar                  , GC.opsAllocate    = allocateCUDABuffer@@ -102,6 +103,7 @@                                 $exp:mem + $exp:idx * sizeof($ty:t),                                 sizeof($ty:t)));                 |]+  GC.stm [C.cstm|if (futhark_context_sync(ctx) != 0) { return 1; }|]   return [C.cexp|$id:val|] readCUDAScalar _ _ _ space _ =   error $ "Cannot write to '" ++ space ++ "' memory space."@@ -184,7 +186,7 @@     cudaSizeClass SizeTile = "tile_size"     cudaSizeClass SizeLocalMemory = "shared_memory"     cudaSizeClass (SizeBespoke x _) = pretty x-callKernel (LaunchKernel name args num_blocks block_size) = do+callKernel (LaunchKernel safety name args num_blocks block_size) = do   args_arr <- newVName "kernel_args"   time_start <- newVName "time_start"   time_end <- newVName "time_end"@@ -202,9 +204,14 @@   let perm_args         | length num_blocks == 3 = [ [C.cinit|&perm[0]|], [C.cinit|&perm[1]|], [C.cinit|&perm[2]|] ]         | otherwise = []-  let args'' = perm_args ++ [ [C.cinit|&$id:a|] | a <- args' ]+      failure_args = take (numFailureParams safety)+                     [[C.cinit|&ctx->global_failure|],+                      [C.cinit|&ctx->failure_is_an_option|],+                      [C.cinit|&ctx->global_failure_args|]]+      args'' = perm_args ++ failure_args ++ [ [C.cinit|&$id:a|] | a <- args' ]       sizes_nonzero = expsNotZero [grid_x, grid_y, grid_z,                       block_x, block_y, block_z]+   GC.stm [C.cstm|     if ($exp:sizes_nonzero) {       int perm[3] = { 0, 1, 2 };@@ -247,6 +254,10 @@                 $string:name, $id:time_end - $id:time_start);       }     }|]++  when (safety >= SafetyFull) $+    GC.stm [C.cstm|ctx->failure_is_an_option = 1;|]+   where     mkDims [] = ([C.cexp|0|] , [C.cexp|0|], [C.cexp|0|])     mkDims [x] = (x, [C.cexp|1|], [C.cexp|1|])
src/Futhark/CodeGen/Backends/CCUDA/Boilerplate.hs view
@@ -8,19 +8,21 @@ import qualified Language.C.Quote.OpenCL as C  import qualified Futhark.CodeGen.Backends.GenericC as GC-import Futhark.Representation.ExplicitMemory hiding (GetSize, CmpSizeLe, GetSizeMax) import Futhark.CodeGen.ImpCode.OpenCL+import Futhark.CodeGen.Backends.COpenCL.Boilerplate (failureSwitch) import Futhark.Util (chunk, zEncodeString)  import qualified Data.Map as M import Data.FileEmbed (embedStringFile) -+errorMsgNumArgs :: ErrorMsg a -> Int+errorMsgNumArgs = length . errorMsgArgTypes -generateBoilerplate :: String -> String -> [String]+generateBoilerplate :: String -> String -> M.Map KernelName Safety                     -> M.Map Name SizeClass+                    -> [FailureMsg]                     -> GC.CompilerM OpenCL () ()-generateBoilerplate cuda_program cuda_prelude kernel_names sizes = do+generateBoilerplate cuda_program cuda_prelude kernels sizes failures = do   GC.earlyDecls [C.cunit|       $esc:("#include <cuda.h>")       $esc:("#include <nvrtc.h>")@@ -32,7 +34,7 @@    generateSizeFuns sizes   cfg <- generateConfigFuns sizes-  generateContextFuns cfg kernel_names sizes+  generateContextFuns cfg kernels sizes failures   where     cuda_h = $(embedStringFile "rts/c/cuda.h")     free_list_h = $(embedStringFile "rts/c/free_list.h")@@ -222,14 +224,15 @@                        }|])   return cfg -generateContextFuns :: String -> [String]+generateContextFuns :: String -> M.Map KernelName Safety                     -> M.Map Name SizeClass+                    -> [FailureMsg]                     -> GC.CompilerM OpenCL () ()-generateContextFuns cfg kernel_names sizes = do+generateContextFuns cfg kernels sizes failures = do   final_inits <- GC.contextFinalInits   (fields, init_fields) <- GC.contextContents-  let kernel_fields = map (\k -> [C.csdecl|typename CUfunction $id:k;|])-                        kernel_names+  let kernel_fields = map (\k -> [C.csdecl|typename CUfunction $id:k;|]) $+                          M.keys kernels    ctx <- GC.publicDef "context" GC.InitDecl $ \s ->     ([C.cedecl|struct $id:s;|],@@ -241,34 +244,50 @@                          char *error;                          $sdecls:fields                          $sdecls:kernel_fields+                         typename CUdeviceptr global_failure;+                         typename CUdeviceptr global_failure_args;                          struct cuda_context cuda;                          struct sizes sizes;+                         // True if a potentially failing kernel has been enqueued.+                         typename int32_t failure_is_an_option;                        };|])    let set_sizes = zipWith (\i k -> [C.cstm|ctx->sizes.$id:k = cfg->sizes[$int:i];|])                           [(0::Int)..] $ M.keys sizes+      max_failure_args =+        foldl max 0 $ map (errorMsgNumArgs . failureError) failures    GC.publicDef_ "context_new" GC.InitDecl $ \s ->     ([C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|],      [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) {-                          struct $id:ctx* ctx = (struct $id:ctx*) malloc(sizeof(struct $id:ctx));-                          if (ctx == NULL) {-                            return NULL;-                          }-                          ctx->profiling = ctx->debugging = ctx->detail_memory = cfg->cu_cfg.debugging;+                 struct $id:ctx* ctx = (struct $id:ctx*) malloc(sizeof(struct $id:ctx));+                 if (ctx == NULL) {+                   return NULL;+                 }+                 ctx->profiling = ctx->debugging = ctx->detail_memory = cfg->cu_cfg.debugging; -                          ctx->cuda.cfg = cfg->cu_cfg;-                          create_lock(&ctx->lock);-                          $stms:init_fields+                 ctx->cuda.cfg = cfg->cu_cfg;+                 create_lock(&ctx->lock); -                          cuda_setup(&ctx->cuda, cuda_program, cfg->nvrtc_opts);-                          $stms:(map (loadKernelByName) kernel_names)+                 ctx->failure_is_an_option = 0;+                 $stms:init_fields -                          $stms:final_inits-                          $stms:set_sizes-                          return ctx;-                       }|])+                 cuda_setup(&ctx->cuda, cuda_program, cfg->nvrtc_opts); +                 typename int32_t no_error = -1;+                 CUDA_SUCCEED(cuMemAlloc(&ctx->global_failure, sizeof(no_error)));+                 CUDA_SUCCEED(cuMemcpyHtoD(ctx->global_failure, &no_error, sizeof(no_error)));+                 // The +1 is to avoid zero-byte allocations.+                 CUDA_SUCCEED(cuMemAlloc(&ctx->global_failure_args, sizeof(int32_t)*($int:max_failure_args+1)));++                 $stms:(map loadKernel (M.toList kernels))++                 $stms:final_inits+                 $stms:set_sizes++                 return ctx;+               }|])+   GC.publicDef_ "context_free" GC.InitDecl $ \s ->     ([C.cedecl|void $id:s(struct $id:ctx* ctx);|],      [C.cedecl|void $id:s(struct $id:ctx* ctx) {@@ -280,10 +299,30 @@   GC.publicDef_ "context_sync" GC.InitDecl $ \s ->     ([C.cedecl|int $id:s(struct $id:ctx* ctx);|],      [C.cedecl|int $id:s(struct $id:ctx* ctx) {-                         CUDA_SUCCEED(cuCtxSynchronize());-                         return 0;-                       }|])+                 CUDA_SUCCEED(cuCtxSynchronize());+                 if (ctx->failure_is_an_option) {+                   // Check for any delayed error.+                   typename int32_t failure_idx;+                   CUDA_SUCCEED(+                     cuMemcpyDtoH(&failure_idx,+                                  ctx->global_failure,+                                  sizeof(int32_t)));+                   ctx->failure_is_an_option = 0; +                   if (failure_idx >= 0) {+                     typename int32_t args[$int:max_failure_args+1];+                     CUDA_SUCCEED(+                       cuMemcpyDtoH(&args,+                                    ctx->global_failure_args,+                                    sizeof(args)));++                     $stm:(failureSwitch failures)++                     return 1;+                   }+                 }+               }|])+   GC.publicDef_ "context_get_error" GC.InitDecl $ \s ->     ([C.cedecl|char* $id:s(struct $id:ctx* ctx);|],      [C.cedecl|char* $id:s(struct $id:ctx* ctx) {@@ -304,6 +343,6 @@                }|])    where-    loadKernelByName name =+    loadKernel (name, _) =       [C.cstm|CUDA_SUCCEED(cuModuleGetFunction(&ctx->$id:name,                 ctx->cuda.module, $string:name));|]
src/Futhark/CodeGen/Backends/COpenCL.hs view
@@ -8,6 +8,7 @@  import Control.Monad hiding (mapM) import Data.List+import qualified Data.Map as M  import qualified Language.C.Syntax as C import qualified Language.C.Quote.OpenCL as C@@ -26,18 +27,19 @@   res <- ImpGen.compileProg prog   case res of     Left err -> return $ Left err-    Right (Program opencl_code opencl_prelude kernel_names types sizes prog') -> do+    Right (Program opencl_code opencl_prelude kernels+           types sizes failures prog') -> do       let cost_centres =             [copyDevToDev, copyDevToHost, copyHostToDev,              copyScalarToDev, copyScalarFromDev]-            ++ kernel_names+            ++ M.keys kernels       Right <$> GC.compileProg operations                 (generateBoilerplate opencl_code opencl_prelude-                 cost_centres kernel_names types sizes)+                 cost_centres kernels types sizes failures)                 include_opencl_h [Space "device", DefaultSpace]                 cliOptions prog'   where operations :: GC.Operations OpenCL ()-        operations = GC.Operations+        operations = GC.defaultOperations                      { GC.opsCompiler = callKernel                      , GC.opsWriteScalar = writeOpenCLScalar                      , GC.opsReadScalar = readOpenCLScalar@@ -56,20 +58,6 @@                                     "#include <CL/cl.h>",                                     "#endif"] -copyDevToDev, copyDevToHost, copyHostToDev, copyScalarToDev, copyScalarFromDev :: String-copyDevToDev = "copy_dev_to_dev"-copyDevToHost = "copy_dev_to_host"-copyHostToDev = "copy_host_to_dev"-copyScalarToDev = "copy_scalar_to_dev"-copyScalarFromDev = "copy_scalar_from_dev"--profilingEvent :: String -> C.Exp-profilingEvent name =-  [C.cexp|ctx->profiling_paused ? NULL-          : opencl_get_event(&ctx->opencl,-                             &ctx->$id:(kernelRuns name),-                             &ctx->$id:(kernelRuntime name))|]- cliOptions :: [Option] cliOptions =   commonOptions ++@@ -141,16 +129,23 @@ writeOpenCLScalar _ _ _ space _ _ =   error $ "Cannot write to '" ++ space ++ "' memory space." +-- It is often faster to do a blocking clEnqueueReadBuffer() than to+-- do an async clEnqueueReadBuffer() followed by a clFinish(), even+-- with an in-order command queue.  This is safe if and only if there+-- are no possible outstanding failures. readOpenCLScalar :: GC.ReadScalar OpenCL () readOpenCLScalar mem i t "device" _ = do   val <- newVName "read_res"   GC.decl [C.cdecl|$ty:t $id:val;|]   GC.stm [C.cstm|OPENCL_SUCCEED_OR_RETURN(-                   clEnqueueReadBuffer(ctx->opencl.queue, $exp:mem, CL_TRUE,+                   clEnqueueReadBuffer(ctx->opencl.queue, $exp:mem,+                                       ctx->failure_is_an_option ? CL_FALSE : CL_TRUE,                                        $exp:i * sizeof($ty:t), sizeof($ty:t),                                        &$id:val,                                        0, NULL, $exp:(profilingEvent copyScalarFromDev)));               |]+  GC.stm [C.cstm|if (ctx->failure_is_an_option &&+                     futhark_context_sync(ctx) != 0) { return 1; }|]   return [C.cexp|$id:val|] readOpenCLScalar _ _ _ space _ =   error $ "Cannot read from '" ++ space ++ "' memory space."@@ -175,10 +170,13 @@   GC.stm [C.cstm|     if ($exp:nbytes > 0) {       OPENCL_SUCCEED_OR_RETURN(-        clEnqueueReadBuffer(ctx->opencl.queue, $exp:srcmem, CL_TRUE,+        clEnqueueReadBuffer(ctx->opencl.queue, $exp:srcmem,+                            ctx->failure_is_an_option ? CL_FALSE : CL_TRUE,                             $exp:srcidx, $exp:nbytes,                             $exp:destmem + $exp:destidx,                             0, NULL, $exp:(profilingEvent copyHostToDev)));+      if (ctx->failure_is_an_option &&+          futhark_context_sync(ctx) != 0) { return 1; }    }   |] copyOpenCLMemory destmem destidx (Space "device") srcmem srcidx DefaultSpace nbytes =@@ -267,12 +265,27 @@   let field = "max_" ++ pretty size_class   in GC.stm [C.cstm|$id:v = ctx->opencl.$id:field;|] -callKernel (LaunchKernel name args num_workgroups workgroup_size) = do-  zipWithM_ setKernelArg [(0::Int)..] args+callKernel (LaunchKernel safety name args num_workgroups workgroup_size) = do++  -- The other failure args are set automatically when the kernel is+  -- first created.+  when (safety == SafetyFull) $+    GC.stm [C.cstm|+      OPENCL_SUCCEED_OR_RETURN(clSetKernelArg(ctx->$id:name, 1,+                                              sizeof(ctx->failure_is_an_option),+                                              &ctx->failure_is_an_option));+    |]++  zipWithM_ setKernelArg [numFailureParams safety..] args   num_workgroups' <- mapM GC.compileExp num_workgroups   workgroup_size' <- mapM GC.compileExp workgroup_size   local_bytes <- foldM localBytes [C.cexp|0|] args+   launchKernel name num_workgroups' workgroup_size' local_bytes++  when (safety >= SafetyFull) $+    GC.stm [C.cstm|ctx->failure_is_an_option = 1;|]+   where setKernelArg i (ValueKArg e bt) = do           v <- GC.compileExpToName "kernel_arg" bt e           GC.stm [C.cstm|
src/Futhark/CodeGen/Backends/COpenCL/Boilerplate.hs view
@@ -2,11 +2,14 @@ {-# LANGUAGE TemplateHaskell #-} module Futhark.CodeGen.Backends.COpenCL.Boilerplate   ( generateBoilerplate+  , profilingEvent+  , copyDevToDev, copyDevToHost, copyHostToDev, copyScalarToDev, copyScalarFromDev    , kernelRuntime   , kernelRuns    , commonOptions+  , failureSwitch   ) where  import Data.FileEmbed@@ -20,14 +23,48 @@ import Futhark.CodeGen.OpenCL.Heuristics import Futhark.Util (chunk, zEncodeString) -generateBoilerplate :: String -> String -> [String] -> [String] -> [PrimType]+errorMsgNumArgs :: ErrorMsg a -> Int+errorMsgNumArgs = length . errorMsgArgTypes++failureSwitch :: [FailureMsg] -> C.Stm+failureSwitch failures =+  let printfEscape = let escapeChar '%' = "%%"+                         escapeChar c = [c]+                     in concatMap escapeChar+      onPart (ErrorString s) = printfEscape s+      onPart ErrorInt32{} = "%d"+      onFailure i (FailureMsg emsg@(ErrorMsg parts) backtrace) =+         let msg = concatMap onPart parts ++ "\n" ++ printfEscape backtrace+             msgargs = [ [C.cexp|args[$int:j]|] | j <- [0..errorMsgNumArgs emsg-1] ]+         in [C.cstm|case $int:i: {ctx->error = msgprintf($string:msg, $args:msgargs); break;}|]+      failure_cases =+        zipWith onFailure [(0::Int)..] failures+  in [C.cstm|switch (failure_idx) { $stms:failure_cases }|]+++copyDevToDev, copyDevToHost, copyHostToDev, copyScalarToDev, copyScalarFromDev :: String+copyDevToDev = "copy_dev_to_dev"+copyDevToHost = "copy_dev_to_host"+copyHostToDev = "copy_host_to_dev"+copyScalarToDev = "copy_scalar_to_dev"+copyScalarFromDev = "copy_scalar_from_dev"++profilingEvent :: String -> C.Exp+profilingEvent name =+  [C.cexp|(ctx->profiling_paused || !ctx->profiling) ? NULL+          : opencl_get_event(&ctx->opencl,+                             &ctx->$id:(kernelRuns name),+                             &ctx->$id:(kernelRuntime name))|]++generateBoilerplate :: String -> String -> [String] -> M.Map KernelName Safety -> [PrimType]                     -> M.Map Name SizeClass+                    -> [FailureMsg]                     -> GC.CompilerM OpenCL () ()-generateBoilerplate opencl_code opencl_prelude profiling_centres kernel_names types sizes = do+generateBoilerplate opencl_code opencl_prelude profiling_centres kernels types sizes failures = do   final_inits <- GC.contextFinalInits    let (ctx_opencl_fields, ctx_opencl_inits, top_decls, later_top_decls) =-        openClDecls profiling_centres kernel_names opencl_code opencl_prelude+        openClDecls profiling_centres kernels opencl_code opencl_prelude    GC.earlyDecls top_decls @@ -240,15 +277,15 @@                          char *error;                          $sdecls:fields                          $sdecls:ctx_opencl_fields+                         typename cl_mem global_failure;+                         typename cl_mem global_failure_args;                          struct opencl_context opencl;                          struct sizes sizes;+                         // True if a potentially failing kernel has been enqueued.+                         typename cl_int failure_is_an_option;                        };|])    mapM_ GC.libDecl later_top_decls-  let set_required_types = [ [C.cstm|required_types |= OPENCL_F64; |]-                           | FloatType Float64 `elem` types ]-      set_sizes = zipWith (\i k -> [C.cstm|ctx->sizes.$id:k = cfg->sizes[$int:i];|])-                          [(0::Int)..] $ M.keys sizes    GC.libDecl [C.cedecl|static void init_context_early(struct $id:cfg *cfg, struct $id:ctx* ctx) {                      ctx->opencl.cfg = cfg->opencl;@@ -265,22 +302,45 @@                               sizeof(struct profiling_record));                      create_lock(&ctx->lock); +                     ctx->failure_is_an_option = 0;                      $stms:init_fields                      $stms:ctx_opencl_inits   }|] +  let set_sizes = zipWith (\i k -> [C.cstm|ctx->sizes.$id:k = cfg->sizes[$int:i];|])+                          [(0::Int)..] $ M.keys sizes+      max_failure_args =+        foldl max 0 $ map (errorMsgNumArgs . failureError) failures+   GC.libDecl [C.cedecl|static int init_context_late(struct $id:cfg *cfg, struct $id:ctx* ctx, typename cl_program prog) {                      typename cl_int error;++                     typename cl_int no_error = -1;+                     ctx->global_failure =+                       clCreateBuffer(ctx->opencl.ctx,+                                      CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR,+                                      sizeof(cl_int), &no_error, &error);+                     OPENCL_SUCCEED_OR_RETURN(error);++                     // The +1 is to avoid zero-byte allocations.+                     ctx->global_failure_args =+                       clCreateBuffer(ctx->opencl.ctx,+                                      CL_MEM_READ_WRITE,+                                      sizeof(cl_int)*($int:max_failure_args+1), NULL, &error);+                     OPENCL_SUCCEED_OR_RETURN(error);+                      // Load all the kernels.-                     $stms:(map (loadKernelByName) kernel_names)+                     $stms:(map loadKernel (M.toList kernels))                       $stms:final_inits-                      $stms:set_sizes                       return 0;   }|] +  let set_required_types = [ [C.cstm|required_types |= OPENCL_F64; |]+                           | FloatType Float64 `elem` types ]+   GC.publicDef_ "context_new" GC.InitDecl $ \s ->     ([C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|],      [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) {@@ -294,8 +354,11 @@                            init_context_early(cfg, ctx);                           typename cl_program prog = setup_opencl(&ctx->opencl, opencl_program, required_types, cfg->build_opts);-                          init_context_late(cfg, ctx, prog);-                          return ctx;+                          if (init_context_late(cfg, ctx, prog) == 0) {+                            return ctx;+                          } else {+                            return NULL;+                          }                        }|])    GC.publicDef_ "context_new_with_command_queue" GC.InitDecl $ \s ->@@ -327,10 +390,36 @@   GC.publicDef_ "context_sync" GC.InitDecl $ \s ->     ([C.cedecl|int $id:s(struct $id:ctx* ctx);|],      [C.cedecl|int $id:s(struct $id:ctx* ctx) {-                         ctx->error = OPENCL_SUCCEED_NONFATAL(clFinish(ctx->opencl.queue));-                         return ctx->error != NULL;-                       }|])+                 // Check for any delayed error.+                 typename cl_int failure_idx = -1;+                 if (ctx->failure_is_an_option) {+                   OPENCL_SUCCEED_OR_RETURN(+                     clEnqueueReadBuffer(ctx->opencl.queue,+                                         ctx->global_failure,+                                         CL_FALSE,+                                         0, sizeof(typename cl_int), &failure_idx,+                                         0, NULL, $exp:(profilingEvent copyScalarFromDev)));+                   ctx->failure_is_an_option = 0;+                 } +                 OPENCL_SUCCEED_OR_RETURN(clFinish(ctx->opencl.queue));++                 if (failure_idx >= 0) {+                   typename cl_int args[$int:max_failure_args+1];+                   OPENCL_SUCCEED_OR_RETURN(+                     clEnqueueReadBuffer(ctx->opencl.queue,+                                         ctx->global_failure_args,+                                         CL_TRUE,+                                         0, sizeof(args), &args,+                                         0, NULL, $exp:(profilingEvent copyDevToHost)));++                   $stm:(failureSwitch failures)++                   return 1;+                 }+                 return 0;+               }|])+   GC.publicDef_ "context_get_error" GC.InitDecl $ \s ->     ([C.cedecl|char* $id:s(struct $id:ctx* ctx);|],      [C.cedecl|char* $id:s(struct $id:ctx* ctx) {@@ -367,9 +456,9 @@   GC.profileReport [C.citem|OPENCL_SUCCEED_FATAL(opencl_tally_profiling_records(&ctx->opencl));|]   mapM_ GC.profileReport $ openClReport profiling_centres -openClDecls :: [String] -> [String] -> String -> String+openClDecls :: [String] -> M.Map KernelName Safety -> String -> String             -> ([C.FieldGroup], [C.Stm], [C.Definition], [C.Definition])-openClDecls profiling_centres kernel_names opencl_program opencl_prelude =+openClDecls profiling_centres kernels opencl_program opencl_prelude =   (ctx_fields, ctx_inits, openCL_boilerplate, openCL_load)   where opencl_program_fragments =           -- Some C compilers limit the size of literal strings, so@@ -381,7 +470,7 @@           [ [C.csdecl|int total_runs;|],             [C.csdecl|long int total_runtime;|] ] ++           [ [C.csdecl|typename cl_kernel $id:name;|]-          | name <- kernel_names ] +++          | name <- M.keys kernels ] ++           concat           [ [ [C.csdecl|typename int64_t $id:(kernelRuntime name);|]             , [C.csdecl|int $id:(kernelRuns name);|]@@ -421,14 +510,27 @@           $esc:openCL_h           static const char *opencl_program[] = {$inits:program_fragments};|] -loadKernelByName :: String -> C.Stm-loadKernelByName name = [C.cstm|{+loadKernel :: (KernelName, Safety) -> C.Stm+loadKernel (name, safety) = [C.cstm|{   ctx->$id:name = clCreateKernel(prog, $string:name, &error);   OPENCL_SUCCEED_FATAL(error);+  $items:set_args   if (ctx->debugging) {     fprintf(stderr, "Created kernel %s.\n", $string:name);   }   }|]+  where set_global_failure =+          [C.citem|OPENCL_SUCCEED_FATAL(+                     clSetKernelArg(ctx->$id:name, 0, sizeof(typename cl_mem),+                                    &ctx->global_failure));|]+        set_global_failure_args =+          [C.citem|OPENCL_SUCCEED_FATAL(+                     clSetKernelArg(ctx->$id:name, 2, sizeof(typename cl_mem),+                                    &ctx->global_failure_args));|]+        set_args = case safety of+                     SafetyNone -> []+                     SafetyCheap -> [set_global_failure]+                     SafetyFull -> [set_global_failure, set_global_failure_args]  kernelRuntime :: String -> String kernelRuntime = (++"_total_runtime")
src/Futhark/CodeGen/Backends/CSOpenCL.hs view
@@ -8,7 +8,7 @@   import Futhark.Error-import Futhark.Representation.ExplicitMemory (Prog, ExplicitMemory)+import Futhark.Representation.ExplicitMemory (Prog, ExplicitMemory, int32) import Futhark.CodeGen.Backends.CSOpenCL.Boilerplate import qualified Futhark.CodeGen.Backends.GenericCSharp as CS import qualified Futhark.CodeGen.ImpCode.OpenCL as Imp@@ -26,7 +26,7 @@   res <- ImpGen.compileProg prog   case res of     Left err -> return $ Left err-    Right (Imp.Program opencl_code opencl_prelude kernel_names types sizes prog') ->+    Right (Imp.Program opencl_code opencl_prelude kernel_names types sizes failures prog') ->       Right <$> CS.compileProg                   module_name                   CS.emptyConstructor@@ -34,7 +34,7 @@                   defines                   operations                   ()-                  (generateBoilerplate opencl_code opencl_prelude kernel_names types sizes)+                  (generateBoilerplate opencl_code opencl_prelude kernel_names types sizes failures)                   []                   [Imp.Space "device", Imp.Space "local", Imp.DefaultSpace]                   cliOptions@@ -135,13 +135,13 @@                      Imp.SizeLocalMemory -> "MaxLocalMemory"                      Imp.SizeBespoke{} -> "MaxBespoke" -callKernel (Imp.LaunchKernel name args num_workgroups workgroup_size) = do+callKernel (Imp.LaunchKernel safety name args num_workgroups workgroup_size) = do   num_workgroups' <- mapM CS.compileExp num_workgroups   workgroup_size' <- mapM CS.compileExp workgroup_size   let kernel_size = zipWith mult_exp num_workgroups' workgroup_size'       total_elements = foldl mult_exp (Integer 1) kernel_size       cond = BinOp "!=" total_elements (Integer 0)-  body <- CS.collect $ launchKernel name kernel_size workgroup_size' args+  body <- CS.collect $ launchKernel safety name kernel_size workgroup_size' args   CS.stm $ If cond body []   where mult_exp = BinOp "*" @@ -150,13 +150,21 @@   CS.stm $ Reassign (Var (CS.compileName v)) $     BinOp "<=" (Field (Var "Ctx.Sizes") (zEncodeString $ pretty key)) x' -launchKernel :: String -> [CSExp] -> [CSExp] -> [Imp.KernelArg] -> CS.CompilerM op s ()-launchKernel kernel_name kernel_dims workgroup_dims args = do+launchKernel :: Imp.Safety -> String -> [CSExp] -> [CSExp] -> [Imp.KernelArg] -> CS.CompilerM op s ()+launchKernel safety kernel_name kernel_dims workgroup_dims args = do   let kernel_name' = "Ctx."++kernel_name-  args_stms <- zipWithM (processKernelArg kernel_name') [0..] args -  CS.stm $ Unsafe $ concat args_stms+  let failure_args =+        [ processMemArg kernel_name' 0 $ Var "Ctx.GlobalFailure"+        , processValueArg kernel_name' 1 int32 $ Var "Ctx.GlobalFailureIsAnOption"+        , processMemArg kernel_name' 2 $ Var "Ctx.GlobalFailureArgs"] +  failure_args' <- concat <$> sequence (take (Imp.numFailureParams safety) failure_args)++  args_stms <- zipWithM (processKernelArg kernel_name')+               [toInteger (Imp.numFailureParams safety)..] args+  CS.stm $ Unsafe $ failure_args' ++ concat args_stms+   global_work_size <- newVName' "GlobalWorkSize"   local_work_size <- newVName' "LocalWorkSize"   stop_watch <- newVName' "StopWatch"@@ -172,9 +180,7 @@    let ctx = (++) "Ctx."   let debugEndStmts =-          [ Exp $ CS.simpleCall "OPENCL_SUCCEED" [-              CS.simpleCall "CL10.Finish"-                [Var "Ctx.OpenCL.Queue"]]+          [ Exp $ CS.simpleCall "FutharkContextSync" []           , Exp $ CallMethod (Var stop_watch) (Var "Stop") []           , Assign (Var time_diff) $ asMicroseconds (Var stop_watch)           , AssignOp "+" (Var $ ctx $ kernelRuntime kernel_name) (Var time_diff)@@ -196,28 +202,37 @@            , Var global_work_size, Var local_work_size, Integer 0, Null, Null]]]      ++      [ If (Var "Ctx.Debugging") debugEndStmts [] ]) []-  finishIfSynchronous -  where processKernelArg :: String-                         -> Integer-                         -> Imp.KernelArg-                         -> CS.CompilerM op s [CSStmt]-        processKernelArg kernel argnum (Imp.ValueKArg e bt) = do-          let t = CS.compilePrimTypeToAST bt-          tmp <- newVName' "kernelArg"-          e' <- CS.compileExp e-          err <- newVName' "setargErr"-          let err_var = Var err-          return [ AssignTyped t (Var tmp) (Just e')-                 , Assign err_var $ getKernelCall kernel argnum (CS.sizeOf t) (Addr $ Var tmp)]+  when (safety >= Imp.SafetyFull) $+    CS.stm $ Reassign (Var "Ctx.GlobalFailureIsAnOption") (Integer 1) -        processKernelArg kernel argnum (Imp.MemKArg v) = do+  finishIfSynchronous++  where processMemArg kernel argnum mem = do           err <- newVName' "setargErr"           dest <- newVName "kArgDest"           let err_var = Var err-          return [ Fixed (Var $ CS.compileName dest) (Addr $ memblockFromMem v)+          return [ Fixed (Var $ CS.compileName dest) (Addr mem)                    [ Assign err_var $ getKernelCall kernel argnum (CS.sizeOf $ Primitive IntPtrT) (Var $ CS.compileName dest)]                  ]++        processValueArg kernel argnum et e = do+          let t = CS.compilePrimTypeToAST et+          tmp <- newVName' "kernelArg"+          err <- newVName' "setargErr"+          let err_var = Var err+          return [ AssignTyped t (Var tmp) (Just e)+                 , Assign err_var $ getKernelCall kernel argnum (CS.sizeOf t) (Addr $ Var tmp)]++        processKernelArg :: String+                         -> Integer+                         -> Imp.KernelArg+                         -> CS.CompilerM op s [CSStmt]+        processKernelArg kernel argnum (Imp.ValueKArg e et) =+          processValueArg kernel argnum et =<< CS.compileExp e++        processKernelArg kernel argnum (Imp.MemKArg v) =+          processMemArg kernel argnum $ memblockFromMem v          processKernelArg kernel argnum (Imp.SharedMemoryKArg (Imp.Count num_bytes)) = do           err <- newVName' "setargErr"
src/Futhark/CodeGen/Backends/CSOpenCL/Boilerplate.hs view
@@ -7,7 +7,7 @@  import qualified Data.Map as M -import Futhark.CodeGen.ImpCode.OpenCL hiding (Index, If)+import Futhark.CodeGen.ImpCode.OpenCL hiding (Index, If, SubExp(..)) import Futhark.CodeGen.Backends.GenericCSharp as CS import Futhark.CodeGen.Backends.GenericCSharp.AST as AST import Futhark.CodeGen.OpenCL.Heuristics@@ -20,14 +20,41 @@ intArrayT = Composite $ ArrayT intT stringArrayT = Composite $ ArrayT stringT -generateBoilerplate :: String -> String -> [String] -> [PrimType]+errorMsgNumArgs :: ErrorMsg a -> Int+errorMsgNumArgs = length . errorMsgArgTypes++formatEscape :: String -> String+formatEscape = concatMap escapeChar+  where escapeChar '{' = "{{"+        escapeChar '}' = "}}"+        escapeChar c = [c]++failureCase :: Integer -> FailureMsg -> CSStmt+failureCase i (FailureMsg (ErrorMsg parts) backtrace) =+  If (BinOp "==" (Var "failure_idx") (Integer i))+  [ let (formatstr, formatargs) = onParts 0 parts+    in AST.Assert (AST.Bool False) $+       String (formatstr ++ "\nBacktrace:\n" ++ formatEscape backtrace) :+       formatargs+  ]+  []+  where onParts _ [] = ("", [])+        onParts j (ErrorString s : parts') =+          let (formatstr, formatargs) = onParts j parts'+          in (s ++ formatstr, formatargs)+        onParts j (ErrorInt32 _ : parts') =+          let (formatstr, formatargs) = onParts (j+1) parts'+          in ("{" ++ show j ++ "}" ++ formatstr, Index (Var "args") (IdxExp $ Integer j) : formatargs)++generateBoilerplate :: String -> String -> M.Map KernelName Safety -> [PrimType]                     -> M.Map Name SizeClass+                    -> [FailureMsg]                     -> CS.CompilerM OpenCL () ()-generateBoilerplate opencl_code opencl_prelude kernel_names types sizes = do+generateBoilerplate opencl_code opencl_prelude kernels types sizes failures = do   final_inits <- CS.contextFinalInits    let (opencl_fields, opencl_inits, top_decls, later_top_decls) =-        openClDecls kernel_names opencl_code opencl_prelude+        openClDecls kernels opencl_code opencl_prelude    CS.stm top_decls @@ -133,6 +160,9 @@     , (Primitive $ CSInt Int64T, "PeakMemUsageDevice")     , (Primitive BoolT, "DetailMemory")     , (Primitive BoolT, "Debugging")+    , (CustomT "CLMemoryHandle", "GlobalFailure")+    , (intT, "GlobalFailureIsAnOption")+    , (CustomT "CLMemoryHandle", "GlobalFailureArgs")     , (CustomT "OpenCLContext", "OpenCL")     , (CustomT "Sizes", "Sizes") ]     ++ opencl_fields@@ -155,7 +185,8 @@       set_sizes = zipWith (\i k -> Reassign (Field (Var "Ctx.Sizes") (zEncodeString $ pretty k))                                             (Index (Var "Cfg.Sizes") (IdxExp $ (Integer . toInteger) i)))                           [(0::Int)..] $ M.keys sizes-+      max_failure_args =+        foldl max 0 $ map (errorMsgNumArgs . failureError) failures    CS.stm $ CS.privateFunDef new_ctx VoidT [(CustomT cfg, "Cfg")] $     [ AssignTyped (CustomT "ComputeErrorCode") (Var "error") Nothing@@ -168,35 +199,80 @@     [ AssignTyped (CustomT "CLProgramHandle") (Var "prog")         (Just $ CS.simpleCall "SetupOpenCL" [ Ref $ Var "Ctx"                                             , Var "OpenCLProgram"-                                            , Var "RequiredTypes"])]-    ++ concatMap loadKernelByName kernel_names+                                            , Var "RequiredTypes"])] +++    [ Reassign (Var "Ctx.GlobalFailureIsAnOption") (Integer 0)+    , Unsafe+      [ Exp $ CS.simpleCall "OPENCL_SUCCEED"+        [CS.simpleCall "OpenCLAllocActual" [ Ref $ Var "Ctx"+                                           , Integer 4+                                           , Ref $ Var "Ctx.GlobalFailure"]]+      , AssignTyped intT (Var "no_failure") (Just (Integer (-1)))+      , Exp $ CS.simpleCall "OPENCL_SUCCEED"+        [CS.simpleCall "CL10.EnqueueWriteBuffer"+         [ Var "Ctx.OpenCL.Queue", Var "Ctx.GlobalFailure", AST.Bool True+         , CS.toIntPtr $ Integer 0+         , CS.toIntPtr $ Integer 4+         , CS.toIntPtr $ Addr (Var "no_failure")+         , Integer 0, Null, Null]]++     , Exp $ CS.simpleCall "OPENCL_SUCCEED"+       [CS.simpleCall "OpenCLAllocActual" [ Ref $ Var "Ctx"+                                          , Integer $ toInteger $ 4 * (max_failure_args + 1)+                                          , Ref $ Var "Ctx.GlobalFailureArgs"]]]]+    ++ concatMap loadKernel (M.toList kernels)     ++ final_inits     ++ set_sizes -  CS.stm $ CS.privateFunDef sync_ctx intT []-    [ Exp $ CS.simpleCall "OPENCL_SUCCEED" [CS.simpleCall "CL10.Finish" [Var "Ctx.OpenCL.Queue"]]-    , Return $ Integer 0 ]+  CS.stm $ CS.privateFunDef sync_ctx VoidT []+    [ AssignTyped intT (Var "failure_idx") (Just $ CS.simpleInitClass (pretty intT) [])+    , Unsafe [ CS.assignScalarPointer (Var "failure_idx") (Var "ptr")+             , Reassign (Var "Ctx.GlobalFailureIsAnOption") (Integer 0)+             , Exp $ CS.simpleCall "OPENCL_SUCCEED" [+                 CS.simpleCall "CL10.EnqueueReadBuffer"+                 [ Var "Ctx.OpenCL.Queue", Var "Ctx.GlobalFailure", AST.Bool True+                 , CS.toIntPtr $ Integer 0+                 , CS.toIntPtr $ Integer 4+                 , CS.toIntPtr $ Var "ptr"+                , Integer 0, Null, Null]+                ]+            ] -  CS.debugReport $ openClReport kernel_names+    , If (BinOp "!=" (Var "failure_idx") (Integer (-1)))+      ([ AssignTyped intArrayT (Var "args") $ Just $ CreateArray intT $+         Left $ max_failure_args + 1+       , Unsafe [+           Fixed (Var "ptr") (Addr (Index (Var "args") $ IdxExp $ Integer 0))+           [Exp $ CS.simpleCall "CL10.EnqueueReadBuffer"+            [ Var "Ctx.OpenCL.Queue", Var "Ctx.GlobalFailureArgs", AST.Bool True+            , CS.toIntPtr $ Integer 0+            , CS.toIntPtr $ Integer $ toInteger $ 4 * max_failure_args+            , CS.toIntPtr $ Var "ptr"+            , Integer 0, Null, Null]]]] +++        zipWith failureCase [0..] failures)+      []+    ] +  CS.debugReport $ openClReport $ M.keys kernels -openClDecls :: [String] -> String -> String++openClDecls :: M.Map KernelName Safety -> String -> String             -> ([(CSType, String)], [CSStmt], CSStmt, [CSStmt])-openClDecls kernel_names opencl_program opencl_prelude =+openClDecls kernels opencl_program opencl_prelude =   (ctx_fields, ctx_inits, openCL_boilerplate, openCL_load)   where ctx_fields =           [ (intT, "TotalRuns")           , (Primitive $ CSInt Int64T, "TotalRuntime")]           ++ concatMap (\name -> [(CustomT "CLKernelHandle", name)                                  ,(longT, kernelRuntime name)-                                 ,(intT, kernelRuns name)]) kernel_names+                                 ,(intT, kernelRuns name)])+          (M.keys kernels)          ctx_inits =           [ Reassign (Var $ ctx "TotalRuns") (Integer 0)           , Reassign (Var $ ctx "TotalRuntime") (Integer 0) ]           ++ concatMap (\name -> [ Reassign (Var $ (ctx . kernelRuntime) name) (Integer 0)-                                 , Reassign (Var $ (ctx . kernelRuns) name) (Integer 0)]-                  ) kernel_names+                                 , Reassign (Var $ (ctx . kernelRuns) name) (Integer 0)])+          (M.keys kernels)           futhark_context = CS.publicName "Context"@@ -209,8 +285,8 @@           AssignTyped stringArrayT (Var "OpenCLProgram")               (Just $ Collection "string[]" [String $ opencl_prelude ++ opencl_program]) -loadKernelByName :: String -> [CSStmt]-loadKernelByName name =+loadKernel :: (String, Safety) -> [CSStmt]+loadKernel (name, _) =   [ Reassign (Var $ ctx name)       (CS.simpleCall "CL10.CreateKernel" [Var "prog", String name, Out $ Var "error"])   , AST.Assert (BinOp "==" (Var "error") (Integer 0)) []
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -13,6 +13,7 @@   , Operations (..)   , defaultOperations   , OpCompiler+  , ErrorCompiler    , PointerQuals   , MemoryType@@ -41,7 +42,6 @@   , compilePrimExp   , compilePrimValue   , compileExpToName-  , dimSizeToExp   , rawMem   , item   , stm@@ -82,7 +82,7 @@ import qualified Language.C.Syntax as C import qualified Language.C.Quote.OpenCL as C -import Futhark.CodeGen.ImpCode hiding (dimSizeToExp)+import Futhark.CodeGen.ImpCode import Futhark.MonadFreshNames import Futhark.CodeGen.Backends.SimpleRepresentation import Futhark.CodeGen.Backends.GenericC.Options@@ -133,6 +133,8 @@ -- compilation function. type OpCompiler op s = op -> CompilerM op s () +type ErrorCompiler op s = ErrorMsg Exp -> String -> CompilerM op s ()+ -- | The address space qualifiers for a pointer of the given type with -- the given annotation. type PointerQuals op s = String -> CompilerM op s [C.TypeQual]@@ -179,12 +181,25 @@               , opsMemoryType :: MemoryType op s              , opsCompiler :: OpCompiler op s+             , opsError :: ErrorCompiler op s               , opsFatMemory :: Bool                -- ^ If true, use reference counting.  Otherwise, bare                -- pointers.              } +defError :: ErrorCompiler op s+defError (ErrorMsg parts) stacktrace = do+  free_all_mem <- collect $ mapM_ (uncurry unRefMem) =<< gets compDeclaredMem+  let onPart (ErrorString s) = return ("%s", [C.cexp|$string:s|])+      onPart (ErrorInt32 x) = ("%d",) <$> compileExp x+  (formatstrs, formatargs) <- unzip <$> mapM onPart parts+  let formatstr = "Error: " ++ concat formatstrs ++ "\n\nBacktrace:\n%s"+  stm [C.cstm|{ ctx->error = msgprintf($string:formatstr, $args:formatargs, $string:stacktrace);+                $items:free_all_mem+                return 1;+              }|]+ -- | A set of operations that fail for every operation involving -- non-default memory spaces.  Uses plain pointers and @malloc@ for -- memory management.@@ -198,6 +213,7 @@                                , opsMemoryType = defMemoryType                                , opsCompiler = defCompiler                                , opsFatMemory = True+                               , opsError = defError                                }   where defWriteScalar _ _ _ _ _ =           error "Cannot write to non-default memory space because I am dumb"@@ -218,6 +234,7 @@         defCompiler _ =           error "The default compiler cannot compile extended operations" + data CompilerEnv op s = CompilerEnv {     envOperations :: Operations op s   , envFtable     :: M.Map Name [Type]@@ -373,6 +390,10 @@   toExp (BoolValue False) = C.toExp (0::Int8)   toExp Checked = C.toExp (1::Int8) +instance C.ToExp SubExp where+  toExp (Var v) = C.toExp v+  toExp (Constant c) = C.toExp c+ -- | Construct a publicly visible definition using the specified name -- as the template.  The first returned definition is put in the -- header file, and the second is the implementation.  Returns the public@@ -446,25 +467,30 @@ rawMemCType :: Space -> CompilerM op s C.Type rawMemCType DefaultSpace = return defaultMemBlockType rawMemCType (Space sid) = join $ asks envMemoryType <*> pure sid+rawMemCType (ScalarSpace [] t) =+  return [C.cty|$ty:(primTypeToCType t)[1]|]+rawMemCType (ScalarSpace ds t) =+  return $ foldl' addDim [C.cty|$ty:(primTypeToCType t)|] ds+  where addDim t' d = [C.cty|$ty:t'[$exp:d]|]  fatMemType :: Space -> C.Type fatMemType space =   [C.cty|struct $id:name|]   where name = case space of-          DefaultSpace -> "memblock"           Space sid    -> "memblock_" ++ sid+          _            -> "memblock"  fatMemSet :: Space -> String-fatMemSet DefaultSpace = "memblock_set" fatMemSet (Space sid) = "memblock_set_" ++ sid+fatMemSet _ = "memblock_set"  fatMemAlloc :: Space -> String-fatMemAlloc DefaultSpace = "memblock_alloc" fatMemAlloc (Space sid) = "memblock_alloc_" ++ sid+fatMemAlloc _ = "memblock_alloc"  fatMemUnRef :: Space -> String-fatMemUnRef DefaultSpace = "memblock_unref" fatMemUnRef (Space sid) = "memblock_unref_" ++ sid+fatMemUnRef _ = "memblock_unref"  rawMem :: C.ToExp a => a -> CompilerM op s C.Exp rawMem v = rawMem' <$> asks envFatMemory <*> pure v@@ -491,7 +517,7 @@   free <- case space of     Space sid -> do free_mem <- asks envDeallocate                     collect $ free_mem [C.cexp|block->mem|] [C.cexp|block->desc|] sid-    DefaultSpace -> return [[C.citem|free(block->mem);|]]+    _ -> return [[C.citem|free(block->mem);|]]   ctx_ty <- contextType   let unrefdef = [C.cedecl|static int $id:(fatMemUnRef space) ($ty:ctx_ty *ctx, $ty:mty *block, const char *desc) {   if (block->references != NULL) {@@ -517,11 +543,11 @@   -- When allocating a memory block we initialise the reference count to 1.   alloc <- collect $     case space of-      DefaultSpace ->-        stm [C.cstm|block->mem = (char*) malloc(size);|]       Space sid ->         join $ asks envAllocate <*> pure [C.cexp|block->mem|] <*>         pure [C.cexp|size|] <*> pure [C.cexp|desc|] <*> pure sid+      _ ->+        stm [C.cstm|block->mem = (char*) malloc(size);|]   let allocdef = [C.cedecl|static int $id:(fatMemAlloc space) ($ty:ctx_ty *ctx, $ty:mty *block, typename int64_t size, const char *desc) {   if (size < 0) {     panic(1, "Negative allocation of %lld bytes attempted for %s in %s.\n",@@ -570,14 +596,14 @@                            (long long) ctx->$id:peakname);|])   where mty = fatMemType space         (peakname, usagename, sname, spacedesc) = case space of-          DefaultSpace -> ("peak_mem_usage_default",-                           "cur_mem_usage_default",-                            "memblock",-                            "default space")           Space sid    -> ("peak_mem_usage_" ++ sid,                            "cur_mem_usage_" ++ sid,                            "memblock_" ++ sid,                            "space '" ++ sid ++ "'")+          _ -> ("peak_mem_usage_default",+                "cur_mem_usage_default",+                "memblock",+                "default space")  declMem :: VName -> Space -> CompilerM op s () declMem name space = do@@ -624,11 +650,11 @@                      }|]     else alloc name   where alloc dest = case space of-          DefaultSpace ->-            stm [C.cstm|$exp:dest = (char*) malloc($exp:size);|]           Space sid ->             join $ asks envAllocate <*> rawMem name <*>             pure [C.cexp|$exp:size|] <*> pure [C.cexp|desc|] <*> pure sid+          _ ->+            stm [C.cstm|$exp:dest = (char*) malloc($exp:size);|]  primTypeInfo :: PrimType -> Signedness -> C.Exp primTypeInfo (IntType it) t = case (it, t) of@@ -908,7 +934,7 @@           stm [C.cstm|$exp:mem = $exp:src->mem;|]            let rank = length shape-              maybeCopyDim (VarSize d) i =+              maybeCopyDim (Var d) i =                 Just [C.cstm|$id:d = $exp:src->shape[$int:i];|]               maybeCopyDim _ _ = Nothing @@ -955,9 +981,9 @@           stm [C.cstm|$exp:dest->mem = $id:mem;|]            let rank = length shape-              maybeCopyDim (ConstSize x) i =-                [C.cstm|$exp:dest->shape[$int:i] = $int:x;|]-              maybeCopyDim (VarSize d) i =+              maybeCopyDim (Constant x) i =+                [C.cstm|$exp:dest->shape[$int:i] = $exp:x;|]+              maybeCopyDim (Var d) i =                 [C.cstm|$exp:dest->shape[$int:i] = $id:d;|]           stms $ zipWith maybeCopyDim shape [0..rank-1] @@ -1167,7 +1193,9 @@                   int r;                   /* Run the program once. */                   $stms:pack_input-                  assert($id:sync_ctx(ctx) == 0);+                  if ($id:sync_ctx(ctx) != 0) {+                    panic(1, "%s", $id:error_ctx(ctx));+                  };                   // Only profile last run.                   if (profile_run) {                     $id:unpause_profiling(ctx);@@ -1179,7 +1207,9 @@                   if (r != 0) {                     panic(1, "%s", $id:error_ctx(ctx));                   }-                  assert($id:sync_ctx(ctx) == 0);+                  if ($id:sync_ctx(ctx) != 0) {+                    panic(1, "%s", $id:error_ctx(ctx));+                  };                   if (profile_run) {                     $id:pause_profiling(ctx);                   }@@ -1575,10 +1605,6 @@ compilePrimValue Checked =   [C.cexp|0|] -dimSizeToExp :: DimSize -> C.Exp-dimSizeToExp (ConstSize x) = [C.cexp|$int:x|]-dimSizeToExp (VarSize v)   = [C.cexp|$exp:v|]- derefPointer :: C.Exp -> C.Exp -> C.Type -> C.Exp derefPointer ptr i res_t =   [C.cexp|(($ty:res_t)$exp:ptr)[$exp:i]|]@@ -1627,6 +1653,11 @@           <*> rawMem src <*> compileExp iexp           <*> pure (primTypeToCType restype) <*> pure space <*> pure vol +        compileLeaf (Index src (Count iexp) _ ScalarSpace{} _) = do+          src' <- rawMem src+          iexp' <- compileExp iexp+          return [C.cexp|$exp:src'[$exp:iexp']|]+         compileLeaf (SizeOf t) =           return [C.cexp|(typename int32_t)sizeof($ty:t')|]           where t' = primTypeToCType t@@ -1689,12 +1720,14 @@ compilePrimExp f (BinOpExp bop x y) = do   x' <- compilePrimExp f x   y' <- compilePrimExp f y+  -- Note that integer addition, subtraction, and multiplication are+  -- not handled by explicit operators, but rather by functions.  This+  -- is because we want to implicitly convert them to unsigned+  -- numbers, so we can do overflow without invoking undefined+  -- behaviour.   return $ case bop of-             Add{} -> [C.cexp|$exp:x' + $exp:y'|]              FAdd{} -> [C.cexp|$exp:x' + $exp:y'|]-             Sub{} -> [C.cexp|$exp:x' - $exp:y'|]              FSub{} -> [C.cexp|$exp:x' - $exp:y'|]-             Mul{} -> [C.cexp|$exp:x' * $exp:y'|]              FMul{} -> [C.cexp|$exp:x' * $exp:y'|]              FDiv{} -> [C.cexp|$exp:x' / $exp:y'|]              Xor{} -> [C.cexp|$exp:x' ^ $exp:y'|]@@ -1748,18 +1781,11 @@  compileCode (c1 :>>: c2) = compileCode c1 >> compileCode c2 -compileCode (Assert e (ErrorMsg parts) (loc, locs)) = do+compileCode (Assert e msg (loc, locs)) = do   e' <- compileExp e-  free_all_mem <- collect $ mapM_ (uncurry unRefMem) =<< gets compDeclaredMem-  let onPart (ErrorString s) = return ("%s", [C.cexp|$string:s|])-      onPart (ErrorInt32 x) = ("%d",) <$> compileExp x-  (formatstrs, formatargs) <- unzip <$> mapM onPart parts-  let formatstr = "Error: " ++ concat formatstrs ++ "\n\nBacktrace:\n%s"-  stm [C.cstm|if (!$exp:e') {-                   ctx->error = msgprintf($string:formatstr, $args:formatargs, $string:stacktrace);-                   $items:free_all_mem-                   return 1;-                 }|]+  err <- collect $ join $+         asks (opsError . envOperations) <*> pure msg <*> pure stacktrace+  stm [C.cstm|if (!$exp:e') { $items:err }|]   where stacktrace = prettyStacktrace 0 $ map locStr $ loc:locs  compileCode (Allocate name (Count e) space) = do@@ -1771,7 +1797,7 @@  compileCode (For i it bound body) = do   let i' = C.toIdent i-      it' = intTypeToCType it+      it' = primTypeToCType $ IntType it   bound' <- compileExp bound   body'  <- blockScope $ compileCode body   stm [C.cstm|for ($ty:it' $id:i' = 0; $id:i' < $exp:bound'; $id:i'++) {@@ -1822,6 +1848,12 @@   elemexp' <- compileExp elemexp   stm [C.cstm|$exp:deref = $exp:elemexp';|] +compileCode (Write dest (Count idx) _ ScalarSpace{} _ elemexp) = do+  dest' <- rawMem dest+  idx' <- compileExp idx+  elemexp' <- compileExp elemexp+  stm [C.cstm|$exp:dest'[$exp:idx'] = $exp:elemexp';|]+ compileCode (Write dest (Count idx) elemtype (Space space) vol elemexp) =   join $ asks envWriteScalar     <*> rawMem dest@@ -1837,6 +1869,9 @@ compileCode (DeclareScalar name vol t) = do   let ct = primTypeToCType t   decl [C.cdecl|$tyquals:(volQuals vol) $ty:ct $id:name;|]++compileCode (DeclareArray name ScalarSpace{} _ _) =+  error $ "Cannot declare array " ++ pretty name ++ " in scalar space."  compileCode (DeclareArray name DefaultSpace t vs) = do   name_realtype <- newVName $ baseString name ++ "_realtype"
src/Futhark/CodeGen/Backends/GenericCSharp.hs view
@@ -609,17 +609,17 @@ compilePrimTypeToASText Imp.Cert _ = Primitive BoolT  compileDim :: Imp.DimSize -> CSExp-compileDim (Imp.ConstSize i) = Integer $ toInteger i-compileDim (Imp.VarSize v) = Var $ compileName v+compileDim (Imp.Constant v) = compilePrimValue v+compileDim (Imp.Var v) = Var $ compileName v  unpackDim :: CSExp -> Imp.DimSize -> Int32 -> CompilerM op s ()-unpackDim arr_name (Imp.ConstSize c) i = do+unpackDim arr_name (Imp.Constant c) i = do   let shape_name = Field arr_name "Item2" -- array tuples are currently (data array * dimension array) currently-  let constant_c = Integer $ toInteger c+  let constant_c = compilePrimValue c   let constant_i = Integer $ toInteger i   stm $ Assert (BinOp "==" constant_c (Index shape_name $ IdxExp constant_i)) [String "constant dimension wrong"] -unpackDim arr_name (Imp.VarSize var) i = do+unpackDim arr_name (Imp.Var var) i = do   let shape_name = Field arr_name "Item2"   let src = Index shape_name $ IdxExp $ Integer $ toInteger i   let dest = Var $ compileName var@@ -639,16 +639,16 @@   return $ cast $ Var $ compileName name   where cast = compileTypecastExt bt ept -entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem Imp.DefaultSpace bt ept dims)) = do-  let src = Var $ compileName mem-  let createTuple = "createTuple_" ++ compilePrimTypeExt bt ept-  return $ simpleCall createTuple [src, CreateArray (Primitive $ CSInt Int64T) $ Right $ map compileDim dims]- entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims)) = do   unRefMem mem (Imp.Space sid)   pack_output <- asks envEntryOutput   pack_output mem sid bt ept dims +entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem _ bt ept dims)) = do+  let src = Var $ compileName mem+  let createTuple = "createTuple_" ++ compilePrimTypeExt bt ept+  return $ simpleCall createTuple [src, CreateArray (Primitive $ CSInt Int64T) $ Right $ map compileDim dims]+ entryPointInput :: (Int, Imp.ExternalValue, CSExp) -> CompilerM op s () entryPointInput (i, Imp.OpaqueValue _ vs, e) =   mapM_ entryPointInput $ zip3 (repeat i) (map Imp.TransparentValue vs) $@@ -659,18 +659,18 @@       cast = compileTypecast bt   stm $ Assign vname' (cast e) -entryPointInput (_, Imp.TransparentValue (Imp.ArrayValue mem Imp.DefaultSpace bt _ dims), e) = do+entryPointInput (_, Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims), e) = do+  unpack_input <- asks envEntryInput+  unpack <- collect $ unpack_input mem sid bt ept dims e+  stms unpack++entryPointInput (_, Imp.TransparentValue (Imp.ArrayValue mem _ bt _ dims), e) = do   zipWithM_ (unpackDim e) dims [0..]   let arrayData = Field e "Item1"   let dest = Var $ compileName mem       unwrap_call = simpleCall "unwrapArray" [arrayData, sizeOf $ compilePrimTypeToAST bt]   stm $ Assign dest unwrap_call -entryPointInput (_, Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims), e) = do-  unpack_input <- asks envEntryInput-  unpack <- collect $ unpack_input mem sid bt ept dims e-  stms unpack- extValueDescName :: Imp.ExternalValue -> String extValueDescName (Imp.TransparentValue v) = extName $ valueDescName v extValueDescName (Imp.OpaqueValue desc []) = extName $ zEncodeString desc@@ -803,7 +803,7 @@                              , Lambda (Tuple [Var "acc", Var "val"])                                       [Exp $ BinOp "*" (Var "acc") (Var "val")]                              ]-      emptystr = "empty(" ++ ppArrayType bt [length dims-1, length dims-2..0] ++ ")"+      emptystr = "empty(" ++ ppArrayType bt [0..length dims-1] ++ ")"    printelem <- printStm (Imp.ArrayValue mem space bt ept shape) ind e   return $@@ -878,11 +878,11 @@           src = name           offset = Integer 0       case space of-        DefaultSpace ->-          stm $ Reassign (Var (compileName name'))-                       (simpleCall "allocateMem" [size]) -- FIXME         Space sid ->           allocate name' size sid+        _ ->+          stm $ Reassign (Var (compileName name'))+                       (simpleCall "allocateMem" [size]) -- FIXME       copy dest offset space src offset space size (IntType Int64) -- FIXME       return $ Just (compileName name')     _ -> return Nothing@@ -1154,20 +1154,21 @@ compileExp (Imp.LeafExp (Imp.SizeOf t) _) =   return $ (compileTypecast $ IntType Int32) (Integer $ primByteSize t) -compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) (IntType Int8) DefaultSpace _) _) = do+compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) restype (Imp.Space space) _) _) =+  join $ asks envReadScalar+    <*> pure src <*> compileExp iexp+    <*> pure restype <*> pure space++compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) (IntType Int8) _ _) _) = do   let src' = compileName src   iexp' <- compileExp iexp   return $ Cast (Primitive $ CSInt Int8T) (Index (Var src') (IdxExp iexp')) -compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) bt DefaultSpace _) _) = do+compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) bt _ _) _) = do   iexp' <- compileExp iexp   let bt' = compilePrimType bt-  return $ simpleCall ("indexArray_" ++ bt') [Var $ compileName src, iexp']--compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) restype (Imp.Space space) _) _) =-  join $ asks envReadScalar-    <*> pure src <*> compileExp iexp-    <*> pure restype <*> pure space+      iexp'' = BinOp "*" iexp' (sizeOf (compilePrimTypeToAST bt))+  return $ simpleCall ("indexArray_" ++ bt') [Var $ compileName src, iexp'']  compileExp (Imp.BinOpExp op x y) = do   (x', y', simple) <- compileBinOpLike x y@@ -1247,7 +1248,11 @@   stm $ AssignTyped t' (Var $ compileName v) Nothing   where t' = compilePrimTypeToAST t -compileCode (Imp.DeclareArray name DefaultSpace t vs) =+compileCode (Imp.DeclareArray name (Space space) t vs) =+  join $ asks envStaticArray <*>+  pure name <*> pure space <*> pure t <*> pure vs++compileCode (Imp.DeclareArray name _ t vs) =   stms [Assign (Var $ "init_"++name') $         simpleCall "unwrapArray"          [@@ -1261,10 +1266,6 @@        ]   where name' = compileName name -compileCode (Imp.DeclareArray name (Space space) t vs) =-  join $ asks envStaticArray <*>-  pure name <*> pure space <*> pure t <*> pure vs- compileCode (Imp.Comment s code) = do   code' <- blockScope $ compileCode code   stm $ Comment s code'@@ -1303,18 +1304,18 @@   let dest' = Var (compileName dest)   stm $ Exp $ simpleCall "MemblockSetDevice" [Ref $ Var "Ctx", Ref dest', Ref src', String (compileName src)] -compileCode (Imp.Allocate name (Imp.Count e) DefaultSpace) = do-  e' <- compileExp e-  let allocate' = simpleCall "allocateMem" [e']-  let name' = Var (compileName name)-  stm $ Reassign name' allocate'- compileCode (Imp.Allocate name (Imp.Count e) (Imp.Space space)) =   join $ asks envAllocate     <*> pure name     <*> compileExp e     <*> pure space +compileCode (Imp.Allocate name (Imp.Count e) _) = do+  e' <- compileExp e+  let allocate' = simpleCall "allocateMem" [e']+  let name' = Var (compileName name)+  stm $ Reassign name' allocate'+ compileCode (Imp.Free name space) = do   unRefMem name space   tell $ mempty { accFreedMem = [name] }@@ -1335,14 +1336,6 @@     <*> pure src <*> compileExp srcoffset <*> pure srcspace     <*> compileExp size <*> pure (IntType Int64) -- FIXME -compileCode (Imp.Write dest (Imp.Count idx) elemtype DefaultSpace _ elemexp) = do-  idx' <- compileExp idx-  elemexp' <- compileExp elemexp-  let dest' = Var $ compileName dest-  let elemtype' = compileTypecast elemtype-  let ctype = elemtype' elemexp'-  stm $ Exp $ simpleCall "writeScalarArray" [dest', idx', ctype]- compileCode (Imp.Write dest (Imp.Count idx) elemtype (Imp.Space space) _ elemexp) =   join $ asks envWriteScalar     <*> pure dest@@ -1351,6 +1344,16 @@     <*> pure space     <*> compileExp elemexp +compileCode (Imp.Write dest (Imp.Count idx) elemtype _ _ elemexp) = do+  idx' <- compileExp idx+  elemexp' <- compileExp elemexp+  let dest' = Var $ compileName dest+      elemtype' = compileTypecast elemtype+      ctype = elemtype' elemexp'+      idx'' = BinOp "*" idx' (sizeOf (compilePrimTypeToAST elemtype))++  stm $ Exp $ simpleCall "writeScalarArray" [dest', idx'', ctype]+ compileCode Imp.Skip = return ()  blockScope :: CompilerM op s () -> CompilerM op s [CSStmt]@@ -1375,7 +1378,7 @@                                                  , (String . compileName) mem] unRefMem _ DefaultSpace = stm Pass unRefMem _ (Space "local") = stm Pass-unRefMem _ (Space _) = error "The default compiler cannot compile unRefMem for other spaces"+unRefMem _ _ = error "The default compiler cannot compile unRefMem for other spaces"   -- | Public names must have a consistent prefix.@@ -1388,14 +1391,14 @@   stm $ declMem' (compileName name) space  declMem' :: String -> Space -> CSStmt-declMem' name DefaultSpace =-  AssignTyped (Composite $ ArrayT $ Primitive ByteT) (Var name) Nothing declMem' name (Space _) =   AssignTyped (CustomT "OpenCLMemblock") (Var name) (Just $ simpleCall "EmptyMemblock" [Var "Ctx.EMPTY_MEM_HANDLE"])+declMem' name _ =+  AssignTyped (Composite $ ArrayT $ Primitive ByteT) (Var name) Nothing  rawMemCSType :: Space -> CSType-rawMemCSType DefaultSpace = Composite $ ArrayT $ Primitive ByteT rawMemCSType (Space _) = CustomT "OpenCLMemblock"+rawMemCSType _ = Composite $ ArrayT $ Primitive ByteT  toIntPtr :: CSExp -> CSExp toIntPtr e = simpleInitClass "IntPtr" [e]
src/Futhark/CodeGen/Backends/GenericPython.hs view
@@ -390,18 +390,17 @@ compileName = zEncodeString . pretty  compileDim :: Imp.DimSize -> PyExp-compileDim (Imp.ConstSize i) = Integer $ toInteger i-compileDim (Imp.VarSize v) = Var $ compileName v+compileDim (Imp.Constant v) = compilePrimValue v+compileDim (Imp.Var v) = Var $ compileName v  unpackDim :: PyExp -> Imp.DimSize -> Int32 -> CompilerM op s ()-unpackDim arr_name (Imp.ConstSize c) i = do+unpackDim arr_name (Imp.Constant c) i = do   let shape_name = Field arr_name "shape"-  let constant_c = Integer $ toInteger c+  let constant_c = compilePrimValue c   let constant_i = Integer $ toInteger i   stm $ Assert (BinOp "==" constant_c (Index shape_name $ IdxExp constant_i)) $     String "constant dimension wrong"--unpackDim arr_name (Imp.VarSize var) i = do+unpackDim arr_name (Imp.Var var) i = do   let shape_name = Field arr_name "shape"       src = Index shape_name $ IdxExp $ Integer $ toInteger i   stm $ Assign (Var $ compileName var) $ simpleCall "np.int32" [src]@@ -413,12 +412,12 @@ entryPointOutput (Imp.TransparentValue (Imp.ScalarValue bt ept name)) =   return $ simpleCall tf [Var $ compileName name]   where tf = compilePrimToExtNp bt ept-entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem Imp.DefaultSpace bt ept dims)) = do-  let cast = Cast (Var $ compileName mem) (compilePrimTypeExt bt ept)-  return $ simpleCall "createArray" [cast, Tuple $ map compileDim dims] entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims)) = do   pack_output <- asks envEntryOutput   pack_output mem sid bt ept dims+entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem _ bt ept dims)) = do+  let cast = Cast (Var $ compileName mem) (compilePrimTypeExt bt ept)+  return $ simpleCall "createArray" [cast, Tuple $ map compileDim dims]  badInput :: Int -> PyExp -> String -> PyStmt badInput i e t =@@ -453,7 +452,15 @@     [Catch (Tuple [Var "TypeError", Var "AssertionError"])      [badInput i e $ prettySigned (s==Imp.TypeUnsigned) bt]] -entryPointInput (i, Imp.TransparentValue (Imp.ArrayValue mem Imp.DefaultSpace t s dims), e) = do+entryPointInput (i, Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims), e) = do+  unpack_input <- asks envEntryInput+  unpack <- collect $ unpack_input mem sid bt ept dims e+  stm $ Try unpack+    [Catch (Tuple [Var "TypeError", Var "AssertionError"])+     [badInput i e $ concat (replicate (length dims) "[]") +++     prettySigned (ept==Imp.TypeUnsigned) bt]]++entryPointInput (i, Imp.TransparentValue (Imp.ArrayValue mem _ t s dims), e) = do   let type_is_wrong =         UnOp "not" $         BinOp "and"@@ -470,14 +477,6 @@    stm $ Assign dest unwrap_call -entryPointInput (i, Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims), e) = do-  unpack_input <- asks envEntryInput-  unpack <- collect $ unpack_input mem sid bt ept dims e-  stm $ Try unpack-    [Catch (Tuple [Var "TypeError", Var "AssertionError"])-     [badInput i e $ concat (replicate (length dims) "[]") ++-     prettySigned (ept==Imp.TypeUnsigned) bt]]- extValueDescName :: Imp.ExternalValue -> String extValueDescName (Imp.TransparentValue v) = extName $ valueDescName v extValueDescName (Imp.OpaqueValue desc []) = extName $ zEncodeString desc@@ -562,11 +561,11 @@           src = name           offset = Integer 0       case space of-        DefaultSpace ->-          stm $ Assign (Var (compileName name'))-                       (simpleCall "allocateMem" [size]) -- FIXME         Space sid ->           allocate name' size sid+        _ ->+          stm $ Assign (Var (compileName name'))+                       (simpleCall "allocateMem" [size]) -- FIXME       copy dest offset space src offset space size (IntType Int32) -- FIXME       return $ Just $ compileName name'     _ -> return Nothing@@ -781,17 +780,17 @@ compileExp (Imp.LeafExp (Imp.SizeOf t) _) =   return $ simpleCall (compilePrimToNp $ IntType Int32) [Integer $ primByteSize t] -compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) bt DefaultSpace _) _) = do-  iexp' <- compileExp iexp-  let bt' = compilePrimType bt-  let nptype = compilePrimToNp bt-  return $ simpleCall "indexArray" [Var $ compileName src, iexp', Var bt', Var nptype]- compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) restype (Imp.Space space) _) _) =   join $ asks envReadScalar     <*> pure src <*> compileExp iexp     <*> pure restype <*> pure space +compileExp (Imp.LeafExp (Imp.Index src (Imp.Count iexp) bt _ _) _) = do+  iexp' <- compileExp iexp+  let bt' = compilePrimType bt+  let nptype = compilePrimToNp bt+  return $ simpleCall "indexArray" [Var $ compileName src, iexp', Var bt', Var nptype]+ compileExp (Imp.BinOpExp op x y) = do   (x', y', simple) <- compileBinOpLike x y   case op of@@ -875,7 +874,11 @@   stm $ Assign (Var $ compileName v) $ Var "True" compileCode Imp.DeclareScalar{} = return () -compileCode (Imp.DeclareArray name DefaultSpace t vs) = do+compileCode (Imp.DeclareArray name (Space space) t vs) =+  join $ asks envStaticArray <*>+  pure name <*> pure space <*> pure t <*> pure vs++compileCode (Imp.DeclareArray name _ t vs) = do   -- It is important to store the Numpy array in a temporary variable   -- to prevent it from going "out-of-scope" before calling   -- unwrapArray (which internally uses the .ctype method); see@@ -896,10 +899,6 @@   where name' = compileName name         arr_name = name' <> "_arr" -compileCode (Imp.DeclareArray name (Space space) t vs) =-  join $ asks envStaticArray <*>-  pure name <*> pure space <*> pure t <*> pure vs- compileCode (Imp.Comment s code) = do   code' <- collect $ compileCode code   stm $ Comment s code'@@ -934,7 +933,13 @@   let dest' = Var (compileName dest)   stm $ Assign dest' src' -compileCode (Imp.Allocate name (Imp.Count e) DefaultSpace) = do+compileCode (Imp.Allocate name (Imp.Count e) (Imp.Space space)) =+  join $ asks envAllocate+    <*> pure name+    <*> compileExp e+    <*> pure space++compileCode (Imp.Allocate name (Imp.Count e) _) = do   e' <- compileExp e   let allocate' = simpleCall "allocateMem" [e']   let name' = Var (compileName name)@@ -943,12 +948,6 @@ compileCode (Imp.Free name _) =   stm $ Assign (Var (compileName name)) None -compileCode (Imp.Allocate name (Imp.Count e) (Imp.Space space)) =-  join $ asks envAllocate-    <*> pure name-    <*> compileExp e-    <*> pure space- compileCode (Imp.Copy dest (Imp.Count destoffset) DefaultSpace src (Imp.Count srcoffset) DefaultSpace (Imp.Count size)) = do   destoffset' <- compileExp destoffset   srcoffset' <- compileExp srcoffset@@ -966,14 +965,6 @@     <*> pure src <*> compileExp srcoffset <*> pure srcspace     <*> compileExp size <*> pure (IntType Int32) -- FIXME -compileCode (Imp.Write dest (Imp.Count idx) elemtype DefaultSpace _ elemexp) = do-  idx' <- compileExp idx-  elemexp' <- compileExp elemexp-  let dest' = Var $ compileName dest-  let elemtype' = compilePrimType elemtype-  let ctype = simpleCall elemtype' [elemexp']-  stm $ Exp $ simpleCall "writeScalarArray" [dest', idx', ctype]- compileCode (Imp.Write dest (Imp.Count idx) elemtype (Imp.Space space) _ elemexp) =   join $ asks envWriteScalar     <*> pure dest@@ -981,5 +972,13 @@     <*> pure elemtype     <*> pure space     <*> compileExp elemexp++compileCode (Imp.Write dest (Imp.Count idx) elemtype _ _ elemexp) = do+  idx' <- compileExp idx+  elemexp' <- compileExp elemexp+  let dest' = Var $ compileName dest+  let elemtype' = compilePrimType elemtype+  let ctype = simpleCall elemtype' [elemexp']+  stm $ Exp $ simpleCall "writeScalarArray" [dest', idx', ctype]  compileCode Imp.Skip = return ()
src/Futhark/CodeGen/Backends/PyOpenCL.hs view
@@ -4,6 +4,7 @@   ) where  import Control.Monad+import qualified Data.Map as M  import Futhark.Error import Futhark.Representation.ExplicitMemory (Prog, ExplicitMemory)@@ -16,7 +17,6 @@ import Futhark.CodeGen.Backends.GenericPython.Definitions import Futhark.MonadFreshNames - --maybe pass the config file rather than multiple arguments compileProg :: MonadFreshNames m =>                Maybe String -> Prog ExplicitMemory ->  m (Either InternalError String)@@ -25,9 +25,12 @@   --could probably be a better why do to this..   case res of     Left err -> return $ Left err-    Right (Imp.Program opencl_code opencl_prelude kernel_names types sizes prog')  -> do+    Right (Imp.Program opencl_code opencl_prelude kernels types sizes failures prog')  -> do       --prepare the strings for assigning the kernels and set them as global-      let assign = unlines $ map (\x -> pretty $ Assign (Var ("self."++x++"_var")) (Var $ "program."++x)) kernel_names+      let assign = unlines $+                   map (\x -> pretty $ Assign (Var ("self."++x++"_var"))+                              (Var $ "program."++x)) $+                   M.keys kernels        let defines =             [Assign (Var "synchronous") $ Bool False,@@ -59,7 +62,7 @@                                        , "default_tile_size=default_tile_size"                                        , "default_threshold=default_threshold"                                        , "sizes=sizes"]-                        [Escape $ openClInit types assign sizes]+                        [Escape $ openClInit types assign sizes failures]           options = [ Option { optionLongName = "platform"                              , optionShortName = Just 'p'                              , optionArgument = RequiredArgument "str"@@ -109,7 +112,7 @@                     ]        Right <$> Py.compileProg module_name constructor imports defines operations ()-        [Exp $ Py.simpleCall "self.queue.finish" []] options prog'+        [Exp $ Py.simpleCall "sync" [Var "self"]] options prog'   where operations :: Py.Operations Imp.OpenCL ()         operations = Py.Operations                      { Py.opsCompiler = callKernel@@ -139,24 +142,34 @@   Py.stm $ Assign (Var (Py.compileName v)) $   Var $ "self.max_" ++ pretty size_class -callKernel (Imp.LaunchKernel name args num_workgroups workgroup_size) = do+callKernel (Imp.LaunchKernel safety name args num_workgroups workgroup_size) = do   num_workgroups' <- mapM (fmap asLong . Py.compileExp) num_workgroups   workgroup_size' <- mapM (fmap asLong . Py.compileExp) workgroup_size   let kernel_size = zipWith mult_exp num_workgroups' workgroup_size'       total_elements = foldl mult_exp (Integer 1) kernel_size       cond = BinOp "!=" total_elements (Integer 0)-  body <- Py.collect $ launchKernel name kernel_size workgroup_size' args++  body <- Py.collect $ launchKernel name safety kernel_size workgroup_size' args   Py.stm $ If cond body []++  when (safety >= Imp.SafetyFull) $+    Py.stm $ Assign (Var "self.failure_is_an_option") $+    Py.compilePrimValue (Imp.IntValue (Imp.Int32Value 1))+   where mult_exp = BinOp "*" -launchKernel :: String -> [PyExp] -> [PyExp] -> [Imp.KernelArg]+launchKernel :: String -> Imp.Safety -> [PyExp] -> [PyExp] -> [Imp.KernelArg]              -> Py.CompilerM op s ()-launchKernel kernel_name kernel_dims workgroup_dims args = do+launchKernel kernel_name safety kernel_dims workgroup_dims args = do   let kernel_dims' = Tuple kernel_dims       workgroup_dims' = Tuple workgroup_dims       kernel_name' = "self." ++ kernel_name ++ "_var"   args' <- mapM processKernelArg args-  Py.stm $ Exp $ Py.simpleCall (kernel_name' ++ ".set_args") args'+  let failure_args = take (Imp.numFailureParams safety)+                     [Var "self.global_failure",+                      Var "self.failure_is_an_option",+                      Var "self.global_failure_args"]+  Py.stm $ Exp $ Py.simpleCall (kernel_name' ++ ".set_args") $ failure_args ++ args'   Py.stm $ Exp $ Py.simpleCall "cl.enqueue_nd_range_kernel"     [Var "self.queue", Var kernel_name', kernel_dims', workgroup_dims']   finishIfSynchronous@@ -194,7 +207,8 @@   Py.stm $ Exp $ Call (Var "cl.enqueue_copy")     [Arg $ Var "self.queue", Arg val', Arg mem',      ArgKeyword "device_offset" $ BinOp "*" (asLong i) (Integer $ Imp.primByteSize bt),-     ArgKeyword "is_blocking" $ Bool True]+     ArgKeyword "is_blocking" $ Var "synchronous"]+  Py.stm $ Exp $ Py.simpleCall "sync" [Var "self"]   return $ Index val' $ IdxExp $ Integer 0  readOpenCLScalar _ _ _ space =@@ -334,4 +348,4 @@  finishIfSynchronous :: Py.CompilerM op s () finishIfSynchronous =-  Py.stm $ If (Var "synchronous") [Exp $ Py.simpleCall "self.queue.finish" []] []+  Py.stm $ If (Var "synchronous") [Exp $ Py.simpleCall "sync" [Var "self"]] []
src/Futhark/CodeGen/Backends/PyOpenCL/Boilerplate.hs view
@@ -11,11 +11,16 @@ import qualified Data.Text as T import NeatInterpolation (text) -import Futhark.CodeGen.ImpCode.OpenCL (PrimType(..), SizeClass(..))+import Futhark.CodeGen.ImpCode.OpenCL+  (PrimType(..), SizeClass(..),+   FailureMsg(..), ErrorMsg(..), ErrorMsgPart(..), errorMsgArgTypes) import Futhark.CodeGen.OpenCL.Heuristics import Futhark.CodeGen.Backends.GenericPython.AST import Futhark.Util.Pretty (prettyText) +errorMsgNumArgs :: ErrorMsg a -> Int+errorMsgNumArgs = length . errorMsgArgTypes+ -- | @rts/python/opencl.py@ embedded as a string. openClPrelude :: String openClPrelude = $(embedStringFile "rts/python/opencl.py")@@ -23,9 +28,11 @@ -- | Python code (as a string) that calls the -- @initiatialize_opencl_object@ procedure.  Should be put in the -- class constructor.-openClInit :: [PrimType] -> String -> M.Map Name SizeClass -> String-openClInit types assign sizes = T.unpack [text|+openClInit :: [PrimType] -> String -> M.Map Name SizeClass -> [FailureMsg] -> String+openClInit types assign sizes failures = T.unpack [text| size_heuristics=$size_heuristics+self.global_failure_args_max = $max_num_args+self.failure_msgs=$failure_msgs program = initialise_opencl_object(self,                                    program_src=fut_opencl_src,                                    command_queue=command_queue,@@ -46,6 +53,19 @@         size_heuristics = prettyText $ sizeHeuristicsToPython sizeHeuristicsTable         types' = prettyText $ map (show . pretty) types -- Looks enough like Python.         sizes' = prettyText $ sizeClassesToPython sizes+        max_num_args = prettyText $ foldl max 0 $ map (errorMsgNumArgs . failureError) failures+        failure_msgs = prettyText $ List $ map formatFailure failures++formatFailure :: FailureMsg -> PyExp+formatFailure (FailureMsg (ErrorMsg parts) backtrace) =+  String $ concatMap onPart parts ++ "\n" ++ formatEscape backtrace+  where formatEscape = let escapeChar '{' = "{{"+                           escapeChar '}' = "}}"+                           escapeChar c = [c]+                       in concatMap escapeChar++        onPart (ErrorString s) = formatEscape s+        onPart ErrorInt32{} = "{}"  sizeClassesToPython :: M.Map Name SizeClass -> PyExp sizeClassesToPython = Dict . map f . M.toList
src/Futhark/CodeGen/Backends/SimpleRepresentation.hs view
@@ -5,8 +5,6 @@   , tupleFieldExp   , funName   , defaultMemBlockType-  , intTypeToCType-  , floatTypeToCType   , primTypeToCType   , signedPrimTypeToCType @@ -104,9 +102,11 @@         taggedI s Int32 = s ++ "32"         taggedI s Int64 = s ++ "64" -        mkAdd = simpleIntOp "add" [C.cexp|x + y|]-        mkSub = simpleIntOp "sub" [C.cexp|x - y|]-        mkMul = simpleIntOp "mul" [C.cexp|x * y|]+        -- Use unsigned types for add/sub/mul so we can do+        -- well-defined overflow.+        mkAdd = simpleUintOp "add" [C.cexp|x + y|]+        mkSub = simpleUintOp "sub" [C.cexp|x - y|]+        mkMul = simpleUintOp "mul" [C.cexp|x * y|]         mkUDiv = simpleUintOp "udiv" [C.cexp|x / y|]         mkUMod = simpleUintOp "umod" [C.cexp|x % y|]         mkUMax = simpleUintOp "umax" [C.cexp|x < y ? y : x|]@@ -204,54 +204,54 @@ cIntPrimFuns =   [C.cunit| $esc:("#if defined(__OPENCL_VERSION__)")-   typename int32_t $id:(funName' "popc8") (typename int8_t x) {+   static typename int32_t $id:(funName' "popc8") (typename int8_t x) {       return popcount(x);    }-   typename int32_t $id:(funName' "popc16") (typename int16_t x) {+   static typename int32_t $id:(funName' "popc16") (typename int16_t x) {       return popcount(x);    }-   typename int32_t $id:(funName' "popc32") (typename int32_t x) {+   static typename int32_t $id:(funName' "popc32") (typename int32_t x) {       return popcount(x);    }-   typename int32_t $id:(funName' "popc64") (typename int64_t x) {+   static typename int32_t $id:(funName' "popc64") (typename int64_t x) {       return popcount(x);    } $esc:("#elif defined(__CUDA_ARCH__)")-   typename int32_t $id:(funName' "popc8") (typename int8_t x) {+   static typename int32_t $id:(funName' "popc8") (typename int8_t x) {       return __popc(zext_i8_i32(x));    }-   typename int32_t $id:(funName' "popc16") (typename int16_t x) {+   static typename int32_t $id:(funName' "popc16") (typename int16_t x) {       return __popc(zext_i16_i32(x));    }-   typename int32_t $id:(funName' "popc32") (typename int32_t x) {+   static typename int32_t $id:(funName' "popc32") (typename int32_t x) {       return __popc(x);    }-   typename int32_t $id:(funName' "popc64") (typename int64_t x) {+   static typename int32_t $id:(funName' "popc64") (typename int64_t x) {       return __popcll(x);    } $esc:("#else")-   typename int32_t $id:(funName' "popc8") (typename int8_t x) {+   static typename int32_t $id:(funName' "popc8") (typename int8_t x) {      int c = 0;      for (; x; ++c) {        x &= x - 1;      }      return c;     }-   typename int32_t $id:(funName' "popc16") (typename int16_t x) {+   static typename int32_t $id:(funName' "popc16") (typename int16_t x) {      int c = 0;      for (; x; ++c) {        x &= x - 1;      }      return c;    }-   typename int32_t $id:(funName' "popc32") (typename int32_t x) {+   static typename int32_t $id:(funName' "popc32") (typename int32_t x) {      int c = 0;      for (; x; ++c) {        x &= x - 1;      }      return c;    }-   typename int32_t $id:(funName' "popc64") (typename int64_t x) {+   static typename int32_t $id:(funName' "popc64") (typename int64_t x) {      int c = 0;      for (; x; ++c) {        x &= x - 1;@@ -261,33 +261,115 @@ $esc:("#endif")  $esc:("#if defined(__OPENCL_VERSION__)")-   typename int32_t $id:(funName' "clz8") (typename int8_t x) {+   static typename uint8_t $id:(funName' "mul_hi8") (typename uint8_t a, typename uint8_t b) {+      return mul_hi(a, b);+   }+   static typename uint16_t $id:(funName' "mul_hi16") (typename uint16_t a, typename uint16_t b) {+      return mul_hi(a, b);+   }+   static typename uint32_t $id:(funName' "mul_hi32") (typename uint32_t a, typename uint32_t b) {+      return mul_hi(a, b);+   }+   static typename uint64_t $id:(funName' "mul_hi64") (typename uint64_t a, typename uint64_t b) {+      return mul_hi(a, b);+   }+$esc:("#elif defined(__CUDA_ARCH__)")+   static typename uint8_t $id:(funName' "mul_hi8") (typename uint8_t a, typename uint8_t b) {+     typename uint16_t aa = a;+     typename uint16_t bb = b;+     return (aa * bb) >> 8;+   }+   static typename uint16_t $id:(funName' "mul_hi16") (typename uint16_t a, typename uint16_t b) {+     typename uint32_t aa = a;+     typename uint32_t bb = b;+     return (aa * bb) >> 16;+   }+   static typename uint32_t $id:(funName' "mul_hi32") (typename uint32_t a, typename uint32_t b) {+      return mulhi(a, b);+   }+   static typename uint64_t $id:(funName' "mul_hi64") (typename uint64_t a, typename uint64_t b) {+      return mul64hi(a, b);+   }+$esc:("#else")+   static typename uint8_t $id:(funName' "mul_hi8") (typename uint8_t a, typename uint8_t b) {+     typename uint16_t aa = a;+     typename uint16_t bb = b;+     return (aa * bb) >> 8;+    }+   static typename uint16_t $id:(funName' "mul_hi16") (typename uint16_t a, typename uint16_t b) {+     typename uint32_t aa = a;+     typename uint32_t bb = b;+     return (aa * bb) >> 16;+    }+   static typename uint32_t $id:(funName' "mul_hi32") (typename uint32_t a, typename uint32_t b) {+     typename uint64_t aa = a;+     typename uint64_t bb = b;+     return (aa * bb) >> 32;+    }+   static typename uint64_t $id:(funName' "mul_hi64") (typename uint64_t a, typename uint64_t b) {+     typename __uint128_t aa = a;+     typename __uint128_t bb = b;+     return (aa * bb) >> 64;+    }+$esc:("#endif")++$esc:("#if defined(__OPENCL_VERSION__)")+   static typename uint8_t $id:(funName' "mad_hi8") (typename uint8_t a, typename uint8_t b, typename uint8_t c) {+      return mad_hi(a, b, c);+   }+   static typename uint16_t $id:(funName' "mad_hi16") (typename uint16_t a, typename uint16_t b, typename uint16_t c) {+      return mad_hi(a, b, c);+   }+   static typename uint32_t $id:(funName' "mad_hi32") (typename uint32_t a, typename uint32_t b, typename uint32_t c) {+      return mad_hi(a, b, c);+   }+   static typename uint64_t $id:(funName' "mad_hi64") (typename uint64_t a, typename uint64_t b, typename uint64_t c) {+      return mad_hi(a, b, c);+   }+$esc:("#else")+   static typename uint8_t $id:(funName' "mad_hi8") (typename uint8_t a, typename uint8_t b, typename uint8_t c) {+     return futrts_mul_hi8(a, b) + c;+    }+   static typename uint16_t $id:(funName' "mad_hi16") (typename uint16_t a, typename uint16_t b, typename uint16_t c) {+     return futrts_mul_hi16(a, b) + c;+    }+   static typename uint32_t $id:(funName' "mad_hi32") (typename uint32_t a, typename uint32_t b, typename uint32_t c) {+     return futrts_mul_hi32(a, b) + c;+    }+   static typename uint64_t $id:(funName' "mad_hi64") (typename uint64_t a, typename uint64_t b, typename uint64_t c) {+     return futrts_mul_hi64(a, b) + c;+    }+$esc:("#endif")+++$esc:("#if defined(__OPENCL_VERSION__)")+   static typename int32_t $id:(funName' "clz8") (typename int8_t x) {       return clz(x);    }-   typename int32_t $id:(funName' "clz16") (typename int16_t x) {+   static typename int32_t $id:(funName' "clz16") (typename int16_t x) {       return clz(x);    }-   typename int32_t $id:(funName' "clz32") (typename int32_t x) {+   static typename int32_t $id:(funName' "clz32") (typename int32_t x) {       return clz(x);    }-   typename int32_t $id:(funName' "clz64") (typename int64_t x) {+   static typename int32_t $id:(funName' "clz64") (typename int64_t x) {       return clz(x);    } $esc:("#elif defined(__CUDA_ARCH__)")-   typename int32_t $id:(funName' "clz8") (typename int8_t x) {+   static typename int32_t $id:(funName' "clz8") (typename int8_t x) {       return __clz(zext_i8_i32(x))-24;    }-   typename int32_t $id:(funName' "clz16") (typename int16_t x) {+   static typename int32_t $id:(funName' "clz16") (typename int16_t x) {       return __clz(zext_i16_i32(x))-16;    }-   typename int32_t $id:(funName' "clz32") (typename int32_t x) {+   static typename int32_t $id:(funName' "clz32") (typename int32_t x) {       return __clz(x);    }-   typename int32_t $id:(funName' "clz64") (typename int64_t x) {+   static typename int32_t $id:(funName' "clz64") (typename int64_t x) {       return __clzll(x);    } $esc:("#else")-   typename int32_t $id:(funName' "clz8") (typename int8_t x) {+   static typename int32_t $id:(funName' "clz8") (typename int8_t x) {     int n = 0;     int bits = sizeof(x) * 8;     for (int i = 0; i < bits; i++) {@@ -297,7 +379,7 @@     }     return n;    }-   typename int32_t $id:(funName' "clz16") (typename int16_t x) {+   static typename int32_t $id:(funName' "clz16") (typename int16_t x) {     int n = 0;     int bits = sizeof(x) * 8;     for (int i = 0; i < bits; i++) {@@ -307,7 +389,7 @@     }     return n;    }-   typename int32_t $id:(funName' "clz32") (typename int32_t x) {+   static typename int32_t $id:(funName' "clz32") (typename int32_t x) {     int n = 0;     int bits = sizeof(x) * 8;     for (int i = 0; i < bits; i++) {@@ -317,7 +399,7 @@     }     return n;    }-   typename int32_t $id:(funName' "clz64") (typename int64_t x) {+   static typename int32_t $id:(funName' "clz64") (typename int64_t x) {     int n = 0;     int bits = sizeof(x) * 8;     for (int i = 0; i < bits; i++) {@@ -428,6 +510,30 @@       return atan(x);     } +    static inline float $id:(funName' "cosh32")(float x) {+      return cosh(x);+    }++    static inline float $id:(funName' "sinh32")(float x) {+      return sinh(x);+    }++    static inline float $id:(funName' "tanh32")(float x) {+      return tanh(x);+    }++    static inline float $id:(funName' "acosh32")(float x) {+      return acosh(x);+    }++    static inline float $id:(funName' "asinh32")(float x) {+      return asinh(x);+    }++    static inline float $id:(funName' "atanh32")(float x) {+      return atanh(x);+    }+     static inline float $id:(funName' "atan2_32")(float x, float y) {       return atan2(x,y);     }@@ -482,6 +588,12 @@     static inline float $id:(funName' "lerp32")(float v0, float v1, float t) {       return mix(v0, v1, t);     }+    static inline float $id:(funName' "mad32")(float a, float b, float c) {+      return mad(a,b,c);+    }+    static inline float $id:(funName' "fma32")(float a, float b, float c) {+      return fma(a,b,c);+    } $esc:("#else")     static inline float fmod32(float x, float y) {       return fmodf(x, y);@@ -498,6 +610,12 @@     static inline float $id:(funName' "lerp32")(float v0, float v1, float t) {       return v0 + (v1-v0)*t;     }+    static inline float $id:(funName' "mad32")(float a, float b, float c) {+      return a*b+c;+    }+    static inline float $id:(funName' "fma32")(float a, float b, float c) {+      return fmaf(a,b,c);+    } $esc:("#endif") |] @@ -547,6 +665,30 @@       return atan(x);     } +    static inline double $id:(funName' "cosh64")(double x) {+      return cosh(x);+    }++    static inline double $id:(funName' "sinh64")(double x) {+      return sinh(x);+    }++    static inline double $id:(funName' "tanh64")(double x) {+      return tanh(x);+    }++    static inline double $id:(funName' "acosh64")(double x) {+      return acosh(x);+    }++    static inline double $id:(funName' "asinh64")(double x) {+      return asinh(x);+    }++    static inline double $id:(funName' "atanh64")(double x) {+      return atanh(x);+    }+     static inline double $id:(funName' "atan2_64")(double x, double y) {       return atan2(x,y);     }@@ -559,6 +701,10 @@       return lgamma(x);     } +    static inline double $id:(funName' "fma64")(double a, double b, double c) {+      return fma(a,b,c);+    }+     static inline double $id:(funName' "round64")(double x) {       return rint(x);     }@@ -597,7 +743,7 @@       return p.t;     } -    static inline float fmod64(float x, float y) {+    static inline double fmod64(double x, double y) {       return fmod(x, y);     } @@ -605,9 +751,15 @@     static inline double $id:(funName' "lerp64")(double v0, double v1, double t) {       return mix(v0, v1, t);     }+    static inline double $id:(funName' "mad64")(double a, double b, double c) {+      return mad(a,b,c);+    } $esc:("#else")     static inline double $id:(funName' "lerp64")(double v0, double v1, double t) {       return v0 + (v1-v0)*t;+    }+    static inline double $id:(funName' "mad64")(double a, double b, double c) {+      return a*b+c;     } $esc:("#endif") |]
src/Futhark/CodeGen/ImpCode.hs view
@@ -16,7 +16,7 @@   , ExternalValue (..)   , Param (..)   , paramName-  , Size (..)+  , SubExp(..)   , MemSize   , DimSize   , Type (..)@@ -33,6 +33,7 @@   , index   , ErrorMsg(..)   , ErrorMsgPart(..)+  , errorMsgArgTypes   , ArrayContents(..)      -- * Typed enumerations@@ -42,11 +43,6 @@   , bytes   , withElemType -    -- * Converting from sizes-  , dimSizeToExp--    -- * Analysis-     -- * Re-exports from other modules.   , module Language.Futhark.Core   , module Futhark.Representation.Primitive@@ -62,19 +58,16 @@ import Language.Futhark.Core import Futhark.Representation.Primitive import Futhark.Representation.AST.Syntax-  (Space(..), SpaceId, ErrorMsg(..), ErrorMsgPart(..))+  (SubExp(..), Space(..), SpaceId,+   ErrorMsg(..), ErrorMsgPart(..), errorMsgArgTypes) import Futhark.Representation.AST.Attributes.Names import Futhark.Representation.AST.Pretty () import Futhark.Analysis.PrimExp import Futhark.Util.Pretty hiding (space) import Futhark.Representation.Kernels.Sizes (Count(..)) -data Size = ConstSize Int64-          | VarSize VName-          deriving (Eq, Show)--type MemSize = Size-type DimSize = Size+type MemSize = SubExp+type DimSize = SubExp  data Type = Scalar PrimType | Mem Space @@ -234,12 +227,6 @@ withElemType (Count e) t =   bytes $ ConvOpExp (SExt Int32 Int64) e * LeafExp (SizeOf t) (IntType Int64) -dimSizeToExp :: DimSize -> Count Elements Exp-dimSizeToExp (VarSize v) =-  elements $ LeafExp (ScalarVar v) (IntType Int32)-dimSizeToExp (ConstSize x) =-  elements $ ValueExp $ IntValue $ Int32Value $ fromIntegral x- var :: VName -> PrimType -> Exp var = LeafExp . ScalarVar @@ -268,12 +255,8 @@           block = indent 2 . stack . map ppr  instance Pretty Param where-  ppr (ScalarParam name ptype) =-    ppr ptype <+> ppr name-  ppr (MemParam name space) =-    text "mem" <> space' <+> ppr name-    where space' = case space of Space s      -> text "@" <> text s-                                 DefaultSpace -> mempty+  ppr (ScalarParam name ptype) = ppr ptype <+> ppr name+  ppr (MemParam name space) = text "mem" <> ppr space <+> ppr name  instance Pretty ValueDesc where   ppr (ScalarValue t ept name) =@@ -281,12 +264,10 @@     where ept' = case ept of TypeUnsigned -> text " (unsigned)"                              TypeDirect   -> mempty   ppr (ArrayValue mem space et ept shape) =-    foldr f (ppr et) shape <+> text "at" <+> ppr mem <> space' <+> ept'+    foldr f (ppr et) shape <+> text "at" <+> ppr mem <> ppr space <+> ept'     where f e s = brackets $ s <> comma <> ppr e           ept' = case ept of TypeUnsigned -> text " (unsigned)"                              TypeDirect   -> mempty-          space' = case space of Space s      -> text "@" <> text s-                                 DefaultSpace -> mempty   instance Pretty ExternalValue where@@ -295,10 +276,6 @@     text "opaque" <+> text desc <+>     nestedBlock "{" "}" (stack $ map ppr vs) -instance Pretty Size where-  ppr (ConstSize x) = ppr x-  ppr (VarSize v)   = ppr v- instance Pretty ArrayContents where   ppr (ArrayValues vs) = braces (commasep $ map ppr vs)   ppr (ArrayZeros n) = braces (text "0") <+> text "*" <+> ppr n@@ -316,7 +293,7 @@     indent 2 (ppr body) </>     text "}"   ppr (DeclareMem name space) =-    text "var" <+> ppr name <> text ": mem" <> parens (ppr space)+    text "var" <+> ppr name <> text ": mem" <> ppr space   ppr (DeclareScalar name vol t) =     text "var" <+> ppr name <> text ":" <+> vol' <> ppr t     where vol' = case vol of Volatile -> text "volatile "@@ -370,10 +347,8 @@   ppr (ScalarVar v) =     ppr v   ppr (Index v is bt space vol) =-    ppr v <> langle <> vol' <> ppr bt <> space' <> rangle <> brackets (ppr is)-    where space' = case space of DefaultSpace -> mempty-                                 Space s      -> text "@" <> text s-          vol' = case vol of Volatile -> text "volatile "+    ppr v <> langle <> vol' <> ppr bt <> ppr space <> rangle <> brackets (ppr is)+    where vol' = case vol of Volatile -> text "volatile "                              Nonvolatile -> mempty    ppr (SizeOf t) =@@ -466,14 +441,14 @@     fvBind (oneName i) $ freeIn' bound <> freeIn' body   freeIn' (While cond body) =     freeIn' cond <> freeIn' body-  freeIn' DeclareMem{} =-    mempty+  freeIn' (DeclareMem _ space) =+    freeIn' space   freeIn' DeclareScalar{} =     mempty   freeIn' DeclareArray{} =     mempty-  freeIn' (Allocate name size _) =-    freeIn' name <> freeIn' size+  freeIn' (Allocate name size space) =+    freeIn' name <> freeIn' size <> freeIn' space   freeIn' (Free name _) =     freeIn' name   freeIn' (Copy dest x _ src y _ n) =@@ -488,8 +463,8 @@     freeIn' dests <> freeIn' args   freeIn' (If cond t f) =     freeIn' cond <> freeIn' t <> freeIn' f-  freeIn' (Assert e _ _) =-    freeIn' e+  freeIn' (Assert e msg _) =+    freeIn' e <> foldMap freeIn' msg   freeIn' (Op op) =     freeIn' op   freeIn' (Comment _ code) =@@ -505,7 +480,3 @@ instance FreeIn Arg where   freeIn' (MemArg m) = freeIn' m   freeIn' (ExpArg e) = freeIn' e--instance FreeIn Size where-  freeIn' (VarSize name) = fvName name-  freeIn' (ConstSize _) = mempty
src/Futhark/CodeGen/ImpCode/Kernels.hs view
@@ -64,6 +64,14 @@               , kernelName :: Name                -- ^ A short descriptive and _unique_ name - should be                -- alphanumeric and without spaces.++              , kernelFailureTolerant :: Bool+                -- ^ If true, this kernel does not need for check+                -- whether we are in a failing state, as it can cope.+                -- Intuitively, it means that the kernel does not+                -- depend on any non-scalar parameters to make control+                -- flow decisions.  Replication, transpose, and copy+                -- kernels are examples of this.               }             deriving (Show) @@ -136,6 +144,7 @@     (text "groups" <+> brace (ppr $ kernelNumGroups kernel) </>      text "group_size" <+> brace (ppr $ kernelGroupSize kernel) </>      text "uses" <+> brace (commasep $ map ppr $ kernelUses kernel) </>+     text "failure_tolerant" <+> ppr (kernelFailureTolerant kernel) </>      text "body" <+> brace (ppr $ kernelBody kernel))  data KernelOp = GetGroupId VName Int@@ -151,6 +160,13 @@               | MemFenceGlobal               | PrivateAlloc VName (Count Bytes Imp.Exp)               | LocalAlloc VName (Count Bytes Imp.Exp)+              | ErrorSync+                -- ^ Perform a local memory barrier and also check+                -- whether any threads have failed an assertion.  Make+                -- sure all threads would reach all 'ErrorSync's if+                -- any of them do.  A failing assertion will jump to+                -- the next following 'ErrorSync', so make sure it's+                -- not inside control flow or similar.               deriving (Show)  -- Atomic operations return the value stored before the update.@@ -210,6 +226,8 @@     ppr name <+> equals <+> text "private_alloc" <> parens (ppr size)   ppr (LocalAlloc name size) =     ppr name <+> equals <+> text "local_alloc" <> parens (ppr size)+  ppr ErrorSync =+    text "error_sync()"   ppr (Atomic _ (AtomicAdd old arr ind x)) =     ppr old <+> text "<-" <+> text "atomic_add" <>     parens (commasep [ppr arr <> brackets (ppr ind), ppr x])
src/Futhark/CodeGen/ImpCode/OpenCL.hs view
@@ -14,7 +14,10 @@        , KernelName        , KernelArg (..)        , OpenCL (..)+       , Safety(..)+       , numFailureParams        , KernelTarget (..)+       , FailureMsg(..)        , module Futhark.CodeGen.ImpCode        , module Futhark.Representation.Kernels.Sizes        )@@ -32,14 +35,22 @@ data Program = Program { openClProgram :: String                        , openClPrelude :: String                          -- ^ Must be prepended to the program.-                       , openClKernelNames :: [KernelName]+                       , openClKernelNames :: M.Map KernelName Safety                        , openClUsedTypes :: [PrimType]                          -- ^ So we can detect whether the device is capable.                        , openClSizes :: M.Map Name SizeClass                          -- ^ Runtime-configurable constants.+                       , openClFailures :: [FailureMsg]+                         -- ^ Assertion failure error messages.                        , hostFunctions :: Functions OpenCL                        } +-- | Something that can go wrong in a kernel.  Part of the machinery+-- for reporting error messages from within kernels.+data FailureMsg = FailureMsg { failureError :: ErrorMsg Exp+                             , failureBacktrace :: String+                             }+ -- | A function calling OpenCL kernels. type Function = Imp.Function OpenCL @@ -58,8 +69,33 @@                  -- ^ Create this much local memory per workgroup.                deriving (Show) +-- | Whether a kernel can potentially fail (because it contains bounds+-- checks and such).+data MayFail = MayFail | CannotFail+             deriving (Show)++-- | Information about bounds checks and how sensitive it is to+-- errors.  Ordered by least demanding to most.+data Safety+  = SafetyNone+    -- ^ Does not need to know if we are in a failing state, and also+    -- cannot fail.+  | SafetyCheap+    -- ^ Needs to be told if there's a global failure, and that's it,+    -- and cannot fail.+  | SafetyFull+    -- ^ Needs all parameters, may fail itself.+    deriving (Eq, Ord, Show)++-- | How many leading failure arguments we must pass when launching a+-- kernel with these safety characteristics.+numFailureParams :: Safety -> Int+numFailureParams SafetyNone = 0+numFailureParams SafetyCheap = 1+numFailureParams SafetyFull = 3+ -- | Host-level OpenCL operation.-data OpenCL = LaunchKernel KernelName [KernelArg] [Exp] [Exp]+data OpenCL = LaunchKernel Safety KernelName [KernelArg] [Exp] [Exp]             | GetSize VName Name             | CmpSizeLe VName Name Exp             | GetSizeMax VName SizeClass
src/Futhark/CodeGen/ImpGen.hs view
@@ -13,8 +13,6 @@   , AllocCompiler   , Operations (..)   , defaultOperations-  , ValueDestination-  , arrayDestination   , MemLocation (..)   , MemEntry (..)   , ScalarEntry (..)@@ -44,7 +42,6 @@     -- * Building Blocks   , ToExp(..)   , compileAlloc-  , subExpToDimSize   , everythingVolatile   , compileBody   , compileBody'@@ -53,15 +50,11 @@   , compileStms   , compileExp   , defCompileExp-  , offsetArray-  , strideArray   , fullyIndexArray   , fullyIndexArray'-  , Imp.dimSizeToExp-  , dimSizeToSubExp   , copy   , copyDWIM-  , copyDWIMDest+  , copyDWIMFix   , copyElementWise   , typeSize @@ -91,7 +84,6 @@ import Control.Monad.State  hiding (mapM, forM, State) import Control.Monad.Writer hiding (mapM, forM) import Control.Monad.Except hiding (mapM, forM)-import qualified Control.Monad.Fail as Fail import Data.Either import Data.Traversable import qualified Data.Map.Strict as M@@ -123,7 +115,6 @@ type CopyCompiler lore op = PrimType                            -> MemLocation                            -> MemLocation-                           -> Count Elements Imp.Exp -- ^ Number of row elements of the source.                            -> ImpM lore op ()  -- | An alternate way of compiling an allocation.@@ -195,9 +186,6 @@                         -- takes care of this array.                       deriving (Show) -arrayDestination :: MemLocation -> ValueDestination-arrayDestination = ArrayDestination . Just- data Env lore op = Env {     envExpCompiler :: ExpCompiler lore op   , envStmsCompiler :: StmsCompiler lore op@@ -240,9 +228,6 @@             MonadWriter (Imp.Code op),             MonadError InternalError) -instance Fail.MonadFail (ImpM lore op) where-  fail = error . ("ImpM.fail: "++)- instance MonadFreshNames (ImpM lore op) where   getNameSource = gets stateNameSource   putNameSource src = modify $ \s -> s { stateNameSource = src }@@ -256,7 +241,7 @@           entryType (ArrayVar _ arrayEntry) =             Array             (entryArrayElemType arrayEntry)-            (Shape $ map dimSizeToSubExp $ entryArrayShape arrayEntry)+            (Shape $ entryArrayShape arrayEntry)             NoUniqueness           entryType (ScalarVar _ scalarEntry) =             Prim $ entryScalarType scalarEntry@@ -326,7 +311,7 @@             -> Prog lore -> m (Either InternalError (Imp.Functions op)) compileProg ops space prog =   modifyNameSource $ \src ->-  case foldM compileFunDef' (newState src) (progFunctions prog) of+  case foldM compileFunDef' (newState src) (progFuns prog) of     Left err -> (Left err, src)     Right s -> (Right $ stateFunctions s, stateNameSource s)   where compileFunDef' s fdef = do@@ -341,10 +326,9 @@     return $ Left $ Imp.ScalarParam name bt   MemMem space ->     return $ Left $ Imp.MemParam name space-  MemArray bt shape _ (ArrayIn mem ixfun) -> do-    shape' <- mapM subExpToDimSize $ shapeDims shape+  MemArray bt shape _ (ArrayIn mem ixfun) ->     return $ Right $ ArrayDecl name bt $-      MemLocation mem shape' $ fmap (toExp' int32) ixfun+    MemLocation mem (shapeDims shape) $ fmap (toExp' int32) ixfun   where name = paramName fparam  data ArrayDecl = ArrayDecl VName PrimType MemLocation@@ -457,11 +441,11 @@               tell ([Imp.ScalarParam out int32],                     M.singleton x $ ScalarDestination out)               put (memseen, M.insert x out arrseen)-              return $ Imp.VarSize out+              return $ Var out             Just out ->-              return $ Imp.VarSize out+              return $ Var out         inspectExtSize (Free se) =-          imp $ subExpToDimSize se+          return se  compileFunDef :: ExplicitMemorish lore =>                  FunDef lore@@ -489,9 +473,10 @@   compileStms (freeIn ses) bnds $     forM_ (zip dests ses) $ \(d, se) -> copyDWIMDest d [] se [] -compileBody' :: (ExplicitMemorish lore, attr ~ LetAttr lore)-             => [Param attr] -> Body lore -> ImpM lore op ()-compileBody' = compileBody . patternFromParams+compileBody' :: [Param attr] -> Body lore -> ImpM lore op ()+compileBody' params (Body _ bnds ses) =+  compileStms (freeIn ses) bnds $+    forM_ (zip params ses) $ \(param, se) -> copyDWIM (paramName param) [] se []  compileLoopBody :: Typed attr => [Param attr] -> Body lore -> ImpM lore op () compileLoopBody mergeparams (Body _ bnds ses) = do@@ -592,7 +577,7 @@     ForLoop i it bound loopvars -> do       let setLoopParam (p,a)             | Prim _ <- paramType p =-                copyDWIM (paramName p) [] (Var a) [Imp.vi32 i]+                copyDWIM (paramName p) [] (Var a) [DimFix $ Imp.vi32 i]             | otherwise =                 return () @@ -647,7 +632,7 @@  defCompileBasicOp (Pattern _ [pe]) (Index src slice)   | Just idxs <- sliceIndices slice =-      copyDWIM (patElemName pe) [] (Var src) $ map (toExp' int32) idxs+      copyDWIM (patElemName pe) [] (Var src) $ map (DimFix . toExp' int32) idxs  defCompileBasicOp _ Index{} =   return ()@@ -658,7 +643,7 @@ defCompileBasicOp (Pattern _ [pe]) (Replicate (Shape ds) se) = do   ds' <- mapM toExp ds   is <- replicateM (length ds) (newVName "i")-  copy_elem <- collect $ copyDWIM (patElemName pe) (map Imp.vi32 is) se []+  copy_elem <- collect $ copyDWIM (patElemName pe) (map (DimFix . Imp.vi32) is) se []   emit $ foldl (.) id (zipWith (`Imp.For` Int32) is ds') copy_elem  defCompileBasicOp _ Scratch{} =@@ -671,7 +656,7 @@   sFor "i" n' $ \i -> do     let i' = ConvOpExp (SExt Int32 it) i     x <- dPrimV "x" $ e' + i' * s'-    copyDWIM (patElemName pe) [i] (Var x) []+    copyDWIM (patElemName pe) [DimFix i] (Var x) []  defCompileBasicOp (Pattern _ [pe]) (Copy src) =   copyDWIM (patElemName pe) [] (Var src) []@@ -696,8 +681,8 @@       let srcloc = entryArrayLocation yentry           rows = case drop i $ entryArrayShape yentry of                   []  -> error $ "defCompileBasicOp Concat: empty array shape for " ++ pretty y-                  r:_ -> unCount $ Imp.dimSizeToExp r-      copy (elemType $ patElemType pe) destloc srcloc $ arrayOuterSize yentry+                  r:_ -> toExp' int32 r+      copy (elemType $ patElemType pe) destloc srcloc       emit $ Imp.SetScalar offs_glb $ Imp.var offs_glb int32 + rows  defCompileBasicOp (Pattern [] [pe]) (ArrayLit es _)@@ -707,14 +692,14 @@       let t = primValueType v       static_array <- newVName "static_array"       emit $ Imp.DeclareArray static_array dest_space t $ Imp.ArrayValues vs-      let static_src = MemLocation static_array [Imp.ConstSize $ fromIntegral $ length es] $+      let static_src = MemLocation static_array [intConst Int32 $ fromIntegral $ length es] $                        IxFun.iota [fromIntegral $ length es]           entry = MemVar Nothing $ MemEntry dest_space       addVar static_array entry-      copy t dest_mem static_src $ fromIntegral $ length es+      copy t dest_mem static_src   | otherwise =     forM_ (zip [0..] es) $ \(i,e) ->-      copyDWIM (patElemName pe) [fromInteger i] e []+      copyDWIM (patElemName pe) [DimFix $ fromInteger i] e []    where isLiteral (Constant v) = Just v         isLiteral _ = Nothing@@ -804,8 +789,7 @@ memBoundToVarEntry e (MemMem space) =   return $ MemVar e $ MemEntry space memBoundToVarEntry e (MemArray bt shape _ (ArrayIn mem ixfun)) = do-  shape' <- mapM subExpToDimSize $ shapeDims shape-  let location = MemLocation mem shape' $ fmap (toExp' int32) ixfun+  let location = MemLocation mem (shapeDims shape) $ fmap (toExp' int32) ixfun   return $ ArrayVar e ArrayEntry { entryArrayLocation = location                                  , entryArrayElemType = bt                                  }@@ -852,16 +836,6 @@         funcallTarget (MemoryDestination name) =           return [name] -subExpToDimSize :: SubExp -> ImpM lore op Imp.DimSize-subExpToDimSize (Var v) =-  return $ Imp.VarSize v-subExpToDimSize (Constant (IntValue (Int64Value i))) =-  return $ Imp.ConstSize $ fromIntegral i-subExpToDimSize (Constant (IntValue (Int32Value i))) =-  return $ Imp.ConstSize $ fromIntegral i-subExpToDimSize Constant{} =-  compilerBugS "Size subexp is not an int32 or int64 constant."- -- | Compile things to 'Imp.Exp'. class ToExp a where   -- | Compile to an 'Imp.Exp', where the type (must must still be a@@ -929,18 +903,15 @@     _              -> compilerBugS $ "Unknown memory block: " ++ pretty name  destinationFromPattern :: ExplicitMemorish lore => Pattern lore -> ImpM lore op Destination-destinationFromPattern pat = fmap (Destination (baseTag <$> maybeHead (patternNames pat))) . mapM inspect $-                             patternElements pat-  where ctx_names = patternContextNames pat-        inspect patElem = do+destinationFromPattern pat =+  fmap (Destination (baseTag <$> maybeHead (patternNames pat))) . mapM inspect $+  patternElements pat+  where inspect patElem = do           let name = patElemName patElem           entry <- lookupVar name           case entry of-            ArrayVar _ (ArrayEntry (MemLocation mem shape ixfun) _) ->-              return $ ArrayDestination $-              if mem `elem` ctx_names-              then Nothing-              else Just $ MemLocation mem shape ixfun+            ArrayVar _ (ArrayEntry MemLocation{} _) ->+              return $ ArrayDestination Nothing             MemVar{} ->               return $ MemoryDestination name @@ -957,8 +928,13 @@                  -> ImpM lore op (VName, Imp.Space, Count Elements Imp.Exp) fullyIndexArray' (MemLocation mem _ ixfun) indices = do   space <- entryMemSpace <$> lookupMemory mem+  let indices' = case space of+                   ScalarSpace ds _ ->+                     let (zero_is, is) = splitFromEnd (length ds) indices+                     in map (const 0) zero_is ++ is+                   _ -> indices   return (mem, space,-          elements $ IxFun.index ixfun indices)+          elements $ IxFun.index ixfun indices')  sliceArray :: MemLocation            -> Slice Imp.Exp@@ -969,40 +945,17 @@         update (_:ds) (DimFix{}:is) = update ds is         update _      _               = [] -offsetArray :: MemLocation-            -> Imp.Exp-            -> MemLocation-offsetArray (MemLocation mem shape ixfun) offset =-  MemLocation mem shape $ IxFun.offsetIndex ixfun offset--strideArray :: MemLocation-            -> Imp.Exp-            -> MemLocation-strideArray (MemLocation mem shape ixfun) stride =-  MemLocation mem shape $ IxFun.strideIndex ixfun stride--arrayOuterSize :: ArrayEntry -> Count Elements Imp.Exp-arrayOuterSize = arrayDimSize 0--arrayDimSize :: Int -> ArrayEntry -> Count Elements Imp.Exp-arrayDimSize i =-  product . map Imp.dimSizeToExp . take 1 . drop i . entryArrayShape- -- More complicated read/write operations that use index functions.  copy :: CopyCompiler lore op-copy bt pat src n = do+copy bt pat src = do   cc <- asks envCopyCompiler-  cc bt pat src n+  cc bt pat src  -- | Use an 'Imp.Copy' if possible, otherwise 'copyElementWise'. defaultCopy :: CopyCompiler lore op-defaultCopy bt dest src n-  | ixFunMatchesInnerShape-      (Shape $ map dimSizeToExp destshape) destIxFun,-    ixFunMatchesInnerShape-      (Shape $ map dimSizeToExp srcshape) srcIxFun,-    Just destoffset <-+defaultCopy bt dest src+  | Just destoffset <-       IxFun.linearWithOffset destIxFun bt_size,     Just srcoffset  <-       IxFun.linearWithOffset srcIxFun bt_size = do@@ -1011,39 +964,40 @@         emit $ Imp.Copy           destmem (bytes destoffset) destspace           srcmem (bytes srcoffset) srcspace $-          (n * row_size) `withElemType` bt+          num_elems `withElemType` bt   | otherwise =-      copyElementWise bt dest src n+      copyElementWise bt dest src   where bt_size = primByteSize bt-        row_size = product $ map Imp.dimSizeToExp $ drop 1 srcshape-        MemLocation destmem destshape destIxFun = dest+        num_elems = Imp.elements $ product $ map (toExp' int32) srcshape+        MemLocation destmem _ destIxFun = dest         MemLocation srcmem srcshape srcIxFun = src  copyElementWise :: CopyCompiler lore op-copyElementWise bt (MemLocation destmem _ destIxFun) (MemLocation srcmem srcshape srcIxFun) n = do-    is <- replicateM (IxFun.rank destIxFun) (newVName "i")+copyElementWise bt dest src = do+    let bounds = map (toExp' int32) $ memLocationShape src+    is <- replicateM (length bounds) (newVName "i")     let ivars = map Imp.vi32 is-        destidx = IxFun.index destIxFun ivars-        srcidx = IxFun.index srcIxFun ivars-        bounds = map unCount $ n : drop 1 (map Imp.dimSizeToExp srcshape)-    srcspace <- entryMemSpace <$> lookupMemory srcmem-    destspace <- entryMemSpace <$> lookupMemory destmem+    (destmem, destspace, destidx) <- fullyIndexArray' dest ivars+    (srcmem, srcspace, srcidx) <- fullyIndexArray' src ivars     vol <- asks envVolatility     emit $ foldl (.) id (zipWith (`Imp.For` Int32) is bounds) $-      Imp.Write destmem (elements destidx) bt destspace vol $-      Imp.index srcmem (elements srcidx) bt srcspace vol+      Imp.Write destmem destidx bt destspace vol $+      Imp.index srcmem srcidx bt srcspace vol  -- | Copy from here to there; both destination and source may be -- indexeded. copyArrayDWIM :: PrimType-              -> MemLocation -> [Imp.Exp]-              -> MemLocation -> [Imp.Exp]+              -> MemLocation -> [DimIndex Imp.Exp]+              -> MemLocation -> [DimIndex Imp.Exp]               -> ImpM lore op (Imp.Code op) copyArrayDWIM bt-  destlocation@(MemLocation _ destshape dest_ixfun) destis-  srclocation@(MemLocation _ srcshape src_ixfun) srcis+  destlocation@(MemLocation _ destshape _) destslice+  srclocation@(MemLocation _ srcshape _) srcslice -  | length srcis == length srcshape, length destis == length destshape = do+  | Just destis <- mapM dimFix destslice,+    Just srcis <- mapM dimFix srcslice,+    length srcis == length srcshape,+    length destis == length destshape = do   (targetmem, destspace, targetoffset) <-     fullyIndexArray' destlocation destis   (srcmem, srcspace, srcoffset) <-@@ -1055,49 +1009,52 @@   | otherwise = do       let destlocation' =             sliceArray destlocation $-            fullSliceNum (IxFun.shape dest_ixfun) $ map DimFix destis+            fullSliceNum (map (toExp' int32) destshape) destslice           srclocation'  =             sliceArray srclocation $-            fullSliceNum (IxFun.shape src_ixfun) $ map DimFix srcis+            fullSliceNum (map (toExp' int32) srcshape) srcslice           destrank = length (memLocationShape destlocation')           srcrank = length (memLocationShape srclocation')       if destrank /= srcrank-        then fail $ "copyArrayDWIM: cannot copy to " +++        then error $ "copyArrayDWIM: cannot copy to " ++              pretty (memLocationName destlocation') ++              " from " ++ pretty (memLocationName srclocation') ++              " because ranks do not match (" ++ pretty destrank ++              " vs " ++ pretty srcrank ++ ")"       else if destlocation' == srclocation'         then return mempty -- Copy would be no-op.-        else collect $ copy bt destlocation' srclocation' $-             product $ map Imp.dimSizeToExp $-             take 1 $ drop (length srcis) srcshape+        else collect $ copy bt destlocation' srclocation'  -- | Like 'copyDWIM', but the target is a 'ValueDestination' -- instead of a variable name.-copyDWIMDest :: ValueDestination -> [Imp.Exp] -> SubExp -> [Imp.Exp]+copyDWIMDest :: ValueDestination -> [DimIndex Imp.Exp] -> SubExp -> [DimIndex Imp.Exp]              -> ImpM lore op ()  copyDWIMDest _ _ (Constant v) (_:_) =   compilerBugS $   unwords ["copyDWIMDest: constant source", pretty v, "cannot be indexed."]-copyDWIMDest pat dest_is (Constant v) [] =-  case pat of-  ScalarDestination name ->-    emit $ Imp.SetScalar name $ Imp.ValueExp v-  MemoryDestination{} ->-    compilerBugS $-    unwords ["copyDWIMDest: constant source", pretty v, "cannot be written to memory destination."]-  ArrayDestination (Just dest_loc) -> do-    (dest_mem, dest_space, dest_i) <--      fullyIndexArray' dest_loc dest_is-    vol <- asks envVolatility-    emit $ Imp.Write dest_mem dest_i bt dest_space vol $ Imp.ValueExp v-  ArrayDestination Nothing ->-    compilerBugS "copyDWIMDest: ArrayDestination Nothing"+copyDWIMDest pat dest_slice (Constant v) [] =+  case mapM dimFix dest_slice of+    Nothing ->+      compilerBugS $+      unwords ["copyDWIMDest: constant source", pretty v, "with slice destination."]+    Just dest_is ->+      case pat of+        ScalarDestination name ->+          emit $ Imp.SetScalar name $ Imp.ValueExp v+        MemoryDestination{} ->+          compilerBugS $+          unwords ["copyDWIMDest: constant source", pretty v, "cannot be written to memory destination."]+        ArrayDestination (Just dest_loc) -> do+          (dest_mem, dest_space, dest_i) <-+            fullyIndexArray' dest_loc dest_is+          vol <- asks envVolatility+          emit $ Imp.Write dest_mem dest_i bt dest_space vol $ Imp.ValueExp v+        ArrayDestination Nothing ->+          compilerBugS "copyDWIMDest: ArrayDestination Nothing"   where bt = primValueType v -copyDWIMDest dest dest_is (Var src) src_is = do+copyDWIMDest dest dest_slice (Var src) src_slice = do   src_entry <- lookupVar src   case (dest, src_entry) of     (MemoryDestination mem, MemVar _ (MemEntry space)) ->@@ -1111,33 +1068,43 @@       compilerBugS $       unwords ["copyDWIMDest: source", pretty src, "is a memory block."] -    (_, ScalarVar _ (ScalarEntry _)) | not $ null src_is ->+    (_, ScalarVar _ (ScalarEntry _)) | not $ null src_slice ->       compilerBugS $-      unwords ["copyDWIMDest: prim-typed source", pretty src, "with nonzero indices", pretty src_is]+      unwords ["copyDWIMDest: prim-typed source", pretty src, "with slice", pretty src_slice] -    (ScalarDestination name, _) | not $ null dest_is ->+    (ScalarDestination name, _) | not $ null dest_slice ->       compilerBugS $-      unwords ["copyDWIMDest: prim-typed target", pretty name, "with nonzero indices", pretty dest_is]+      unwords ["copyDWIMDest: prim-typed target", pretty name, "with slice", pretty dest_slice]      (ScalarDestination name, ScalarVar _ (ScalarEntry pt)) ->       emit $ Imp.SetScalar name $ Imp.var src pt -    (ScalarDestination name, ArrayVar _ arr) -> do-      let bt = entryArrayElemType arr-      (mem, space, i) <--        fullyIndexArray' (entryArrayLocation arr) src_is-      vol <- asks envVolatility-      emit $ Imp.SetScalar name $ Imp.index mem i bt space vol+    (ScalarDestination name, ArrayVar _ arr)+      | Just src_is <- mapM dimFix src_slice -> do+          let bt = entryArrayElemType arr+          (mem, space, i) <-+            fullyIndexArray' (entryArrayLocation arr) src_is+          vol <- asks envVolatility+          emit $ Imp.SetScalar name $ Imp.index mem i bt space vol+      | otherwise ->+          compilerBugS $+          unwords ["copyDWIMDest: prim-typed target and array-typed source", pretty src,+                   "with slice", pretty src_slice]      (ArrayDestination (Just dest_loc), ArrayVar _ src_arr) -> do       let src_loc = entryArrayLocation src_arr           bt = entryArrayElemType src_arr-      emit =<< copyArrayDWIM bt dest_loc dest_is src_loc src_is+      emit =<< copyArrayDWIM bt dest_loc dest_slice src_loc src_slice -    (ArrayDestination (Just dest_loc), ScalarVar _ (ScalarEntry bt)) -> do-      (dest_mem, dest_space, dest_i) <- fullyIndexArray' dest_loc dest_is-      vol <- asks envVolatility-      emit $ Imp.Write dest_mem dest_i bt dest_space vol (Imp.var src bt)+    (ArrayDestination (Just dest_loc), ScalarVar _ (ScalarEntry bt))+      | Just dest_is <- mapM dimFix dest_slice -> do+          (dest_mem, dest_space, dest_i) <- fullyIndexArray' dest_loc dest_is+          vol <- asks envVolatility+          emit $ Imp.Write dest_mem dest_i bt dest_space vol (Imp.var src bt)+      | otherwise ->+          compilerBugS $+          unwords ["copyDWIMDest: array-typed target and prim-typed source", pretty src,+                   "with slice", pretty dest_slice]      (ArrayDestination Nothing, _) ->       return () -- Nothing to do; something else set some memory@@ -1147,9 +1114,9 @@ -- indexeded.  If so, they better be arrays of enough dimensions. -- This function will generally just Do What I Mean, and Do The Right -- Thing.  Both destination and source must be in scope.-copyDWIM :: VName -> [Imp.Exp] -> SubExp -> [Imp.Exp]+copyDWIM :: VName -> [DimIndex Imp.Exp] -> SubExp -> [DimIndex Imp.Exp]          -> ImpM lore op ()-copyDWIM dest dest_is src src_is = do+copyDWIM dest dest_slice src src_slice = do   dest_entry <- lookupVar dest   let dest_target =         case dest_entry of@@ -1161,8 +1128,13 @@            MemVar _ _ ->             MemoryDestination dest-  copyDWIMDest dest_target dest_is src src_is+  copyDWIMDest dest_target dest_slice src src_slice +-- | As 'copyDWIM', but implicitly 'DimFix'es the indexes.+copyDWIMFix :: VName -> [Imp.Exp] -> SubExp -> [Imp.Exp] -> ImpM lore op ()+copyDWIMFix dest dest_is src src_is =+  copyDWIM dest (map DimFix dest_is) src (map DimFix src_is)+ -- | @compileAlloc pat size space@ allocates @n@ bytes of memory in @space@, -- writing the result to @dest@, which must be a single -- 'MemoryDestination',@@ -1177,13 +1149,6 @@     Just allocator' -> allocator' (patElemName mem) e' compileAlloc pat _ _ =   compilerBugS $ "compileAlloc: Invalid pattern: " ++ pretty pat--dimSizeToSubExp :: Imp.Size -> SubExp-dimSizeToSubExp (Imp.ConstSize n) = constant n-dimSizeToSubExp (Imp.VarSize v) = Var v--dimSizeToExp :: Imp.Size -> Imp.Exp-dimSizeToExp = toExp' int32 . primExpFromSubExp int32 . dimSizeToSubExp  -- | The number of bytes needed to represent the array in a -- straightforward contiguous format.
src/Futhark/CodeGen/ImpGen/Kernels.hs view
@@ -133,9 +133,8 @@  callKernelCopy :: CopyCompiler ExplicitMemory Imp.HostOp callKernelCopy bt-  destloc@(MemLocation destmem destshape destIxFun)+  destloc@(MemLocation destmem _ destIxFun)   srcloc@(MemLocation srcmem srcshape srcIxFun)-  n   | Just (destoffset, srcoffset,           num_arrays, size_x, size_y,           src_elems, dest_elems) <- isMapTransposeKernel bt destloc srcloc = do@@ -148,23 +147,19 @@          Imp.ExpArg src_elems, Imp.ExpArg dest_elems]    | bt_size <- primByteSize bt,-    ixFunMatchesInnerShape-      (Shape $ map (Imp.unCount . Imp.dimSizeToExp) destshape) destIxFun,-    ixFunMatchesInnerShape-      (Shape $ map (Imp.unCount . Imp.dimSizeToExp) srcshape) srcIxFun,     Just destoffset <-       IxFun.linearWithOffset destIxFun bt_size,     Just srcoffset  <-       IxFun.linearWithOffset srcIxFun bt_size = do-        let row_size = product $ map dimSizeToExp $ drop 1 srcshape+        let num_elems = Imp.elements $ product $ map (toExp' int32) srcshape         srcspace <- entryMemSpace <$> lookupMemory srcmem         destspace <- entryMemSpace <$> lookupMemory destmem         emit $ Imp.Copy           destmem (bytes destoffset) destspace           srcmem (bytes srcoffset) srcspace $-          (n * row_size) `Imp.withElemType` bt+          num_elems `Imp.withElemType` bt -  | otherwise = sCopy bt destloc srcloc n+  | otherwise = sCopy bt destloc srcloc  mapTransposeForType :: PrimType -> CallKernelGen Name mapTransposeForType bt = do@@ -294,26 +289,24 @@   (MemLocation _ _ srcIxFun)   | Just (dest_offset, perm_and_destshape) <- IxFun.rearrangeWithOffset destIxFun bt_size,     (perm, destshape) <- unzip perm_and_destshape,-    srcshape' <- IxFun.shape srcIxFun,     Just src_offset <- IxFun.linearWithOffset srcIxFun bt_size,     Just (r1, r2, _) <- isMapTranspose perm =-    isOk (product srcshape') (product destshape) destshape swap r1 r2 dest_offset src_offset+      isOk (product destshape) destshape swap r1 r2 dest_offset src_offset   | Just dest_offset <- IxFun.linearWithOffset destIxFun bt_size,     Just (src_offset, perm_and_srcshape) <- IxFun.rearrangeWithOffset srcIxFun bt_size,     (perm, srcshape) <- unzip perm_and_srcshape,-    destshape' <- IxFun.shape destIxFun,     Just (r1, r2, _) <- isMapTranspose perm =-    isOk (product srcshape) (product destshape') srcshape id r1 r2 dest_offset src_offset+      isOk (product srcshape) srcshape id r1 r2 dest_offset src_offset   | otherwise =-    Nothing+      Nothing   where bt_size = primByteSize bt         swap (x,y) = (y,x) -        isOk src_elems dest_elems shape f r1 r2 dest_offset src_offset = do+        isOk elems shape f r1 r2 dest_offset src_offset = do           let (num_arrays, size_x, size_y) = getSizes shape f r1 r2           return (dest_offset, src_offset,                   num_arrays, size_x, size_y,-                  src_elems, dest_elems)+                  elems, elems)          getSizes shape f r1 r2 =           let (mapped, notmapped) = splitAt r1 shape
src/Futhark/CodeGen/ImpGen/Kernels/Base.hs view
@@ -38,7 +38,6 @@ import Data.Maybe import qualified Data.Map.Strict as M import Data.List-import Data.Loc  import Prelude hiding (quot, rem) @@ -74,15 +73,6 @@ keyWithEntryPoint fname key =   nameFromString $ nameToString fname ++ "." ++ nameToString key -noAssert :: MonadError InternalError m => [SrcLoc] -> m a-noAssert locs =-  compilerLimitationS $-  unlines [ "Cannot compile assertion at " ++-            intercalate " -> " (reverse $ map locStr locs) ++-            " inside parallel kernel."-          , "As a workaround, surround the expression with 'unsafe'."]-- allocLocal, allocPrivate :: AllocCompiler ExplicitMemory Imp.KernelOp allocLocal mem size =   sOp $ Imp.LocalAlloc mem size@@ -93,11 +83,10 @@             -> Pattern ExplicitMemory             -> SubExp -> Space             -> ImpM ExplicitMemory Imp.KernelOp ()-kernelAlloc _ (Pattern _ [_]) _ (Space space)-  | space `M.member` allScalarMemory =-      return () -- Handled by the declaration of the memory block,-                -- which is then translated to an actual scalar-                -- variable during C code generation.+kernelAlloc _ (Pattern _ [_]) _ ScalarSpace{} =+  -- Handled by the declaration of the memory block, which is then+  -- translated to an actual scalar variable during C code generation.+  return () kernelAlloc _ (Pattern _ [mem]) size (Space "private") = do   size' <- toExp size   allocPrivate (patElemName mem) $ Imp.bytes size'@@ -121,10 +110,9 @@   compilerBugS $ "Invalid target for splitSpace: " ++ pretty pat  compileThreadExp :: ExpCompiler ExplicitMemory Imp.KernelOp-compileThreadExp _ (BasicOp (Assert _ _ (loc, locs))) = noAssert $ loc:locs compileThreadExp (Pattern _ [dest]) (BasicOp (ArrayLit es _)) =   forM_ (zip [0..] es) $ \(i,e) ->-  copyDWIM (patElemName dest) [fromIntegral (i::Int32)] e []+  copyDWIMFix (patElemName dest) [fromIntegral (i::Int32)] e [] compileThreadExp dest e =   defCompileExp dest e @@ -165,14 +153,13 @@ groupCopy constants to to_is from from_is = do   ds <- mapM toExp . arrayDims =<< subExpType from   groupCoverSpace constants ds $ \is ->-    copyDWIM to (to_is++ is) from (from_is ++ is)+    copyDWIMFix to (to_is++ is) from (from_is ++ is)  compileGroupExp :: KernelConstants -> ExpCompiler ExplicitMemory Imp.KernelOp-compileGroupExp _ _ (BasicOp (Assert _ _ (loc, locs))) = noAssert $ loc:locs -- The static arrays stuff does not work inside kernels. compileGroupExp _ (Pattern _ [dest]) (BasicOp (ArrayLit es _)) =   forM_ (zip [0..] es) $ \(i,e) ->-  copyDWIM (patElemName dest) [fromIntegral (i::Int32)] e []+  copyDWIMFix (patElemName dest) [fromIntegral (i::Int32)] e [] compileGroupExp constants (Pattern _ [dest]) (BasicOp (Copy arr)) = do   groupCopy constants (patElemName dest) [] (Var arr) []   sOp Imp.LocalBarrier@@ -182,7 +169,7 @@ compileGroupExp constants (Pattern _ [dest]) (BasicOp (Replicate ds se)) = do   ds' <- mapM toExp $ shapeDims ds   groupCoverSpace constants ds' $ \is ->-    copyDWIM (patElemName dest) is se (drop (shapeRank ds) is)+    copyDWIMFix (patElemName dest) is se (drop (shapeRank ds) is)   sOp Imp.LocalBarrier compileGroupExp constants (Pattern _ [dest]) (BasicOp (Iota n e s _)) = do   n' <- toExp n@@ -190,7 +177,7 @@   s' <- toExp s   groupLoop constants n' $ \i' -> do     x <- dPrimV "x" $ e' + i' * s'-    copyDWIM (patElemName dest) [i'] (Var x) []+    copyDWIMFix (patElemName dest) [i'] (Var x) []   sOp Imp.LocalBarrier  compileGroupExp _ dest e =@@ -198,9 +185,8 @@  sanityCheckLevel :: SegLevel -> InKernelGen () sanityCheckLevel SegThread{} = return ()-sanityCheckLevel SegThreadScalar{} = return () sanityCheckLevel SegGroup{} =-  fail "compileGroupOp: unexpected group-level SegOp."+  error "compileGroupOp: unexpected group-level SegOp."  compileGroupSpace :: KernelConstants -> SegLevel -> SegSpace -> InKernelGen () compileGroupSpace constants lvl space = do@@ -245,7 +231,7 @@            sComment "All locks start out unlocked" $             groupCoverSpace constants [kernelGroupSize constants] $ \is ->-            copyDWIM locks is (intConst Int32 0) []+            copyDWIMFix locks is (intConst Int32 0) []            return (Just l', f l' (Space "local") local_subhistos) @@ -265,7 +251,7 @@     zipWithM_ (compileThreadResult space constants) (patternElements pat) $     kernelBodyResult body -  sOp Imp.LocalBarrier+  sOp Imp.ErrorSync  compileGroupOp constants pat (Inner (SegOp (SegScan lvl space scan_op _ _ body))) = do   compileGroupSpace constants lvl space@@ -275,11 +261,11 @@   sWhen (isActive $ unSegSpace space) $     compileStms mempty (kernelBodyStms body) $     forM_ (zip (patternNames pat) $ kernelBodyResult body) $ \(dest, res) ->-    copyDWIM dest+    copyDWIMFix dest     (map (`Imp.var` int32) ltids)     (kernelResultSubExp res) [] -  sOp Imp.LocalBarrier+  sOp Imp.ErrorSync    let segment_size = last dims'       crossesSegment from to = (to-from) .>. (to `rem` segment_size)@@ -305,10 +291,10 @@     let (red_res, map_res) =           splitAt (segRedResults ops) $ kernelBodyResult body     forM_ (zip tmp_arrs red_res) $ \(dest, res) ->-      copyDWIM dest (map (`Imp.var` int32) ltids) (kernelResultSubExp res) []+      copyDWIMFix dest (map (`Imp.var` int32) ltids) (kernelResultSubExp res) []     zipWithM_ (compileThreadResult space constants) map_pes map_res -  sOp Imp.LocalBarrier+  sOp Imp.ErrorSync    case dims' of     -- Nonsegmented case (or rather, a single segment) - this we can@@ -317,10 +303,10 @@       forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->         groupReduce constants dim' (segRedLambda op) tmps -      sOp Imp.LocalBarrier+      sOp Imp.ErrorSync        forM_ (zip red_pes tmp_arrs) $ \(pe, arr) ->-        copyDWIM (patElemName pe) [] (Var arr) [0]+        copyDWIMFix (patElemName pe) [] (Var arr) [0]      _ -> do       -- Segmented intra-group reductions are turned into (regular)@@ -332,11 +318,11 @@       forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->         groupScan constants (Just crossesSegment) (product dims') (segRedLambda op) tmps -      sOp Imp.LocalBarrier+      sOp Imp.ErrorSync        let segment_is = map Imp.vi32 $ init ltids       forM_ (zip red_pes tmp_arrs) $ \(pe, arr) ->-        copyDWIM (patElemName pe) segment_is (Var arr) (segment_is ++ [last dims'-1])+        copyDWIMFix (patElemName pe) segment_is (Var arr) (segment_is ++ [last dims'-1])        sOp Imp.LocalBarrier @@ -377,10 +363,10 @@           dLParams $ lambdaParams lam           sLoopNest shape $ \is -> do             forM_ (zip vs_params op_vs) $ \(p, v) ->-              copyDWIM (paramName p) [] v is+              copyDWIMFix (paramName p) [] v is             do_op (bin_is ++ is) -  sOp Imp.LocalBarrier+  sOp Imp.ErrorSync  compileGroupOp _ pat _ =   compilerBugS $ "compileGroupOp: cannot compile rhs of binding " ++ pretty pat@@ -511,7 +497,7 @@         everythingVolatile $         sComment "bind lhs" $         forM_ (zip acc_params arrs) $ \(acc_p, arr) ->-        copyDWIM (paramName acc_p) [] (Var arr) bucket+        copyDWIMFix (paramName acc_p) [] (Var arr) bucket    let op_body = sComment "execute operation" $                 compileBody' acc_params $ lambdaBody op@@ -537,7 +523,7 @@       release_lock       break_loop     fence-  where writeArray bucket arr val = copyDWIM arr bucket val []+  where writeArray bucket arr val = copyDWIMFix arr bucket val []  atomicUpdateCAS :: Space -> PrimType                 -> VName -> VName@@ -559,7 +545,7 @@   -- XXX: CUDA may generate really bad code if this is not a volatile   -- read.  Unclear why.  The later reads are volatile, so maybe   -- that's it.-  everythingVolatile $ copyDWIM old [] (Var arr) bucket+  everythingVolatile $ copyDWIMFix old [] (Var arr) bucket    (arr', _a_space, bucket_offset) <- fullyIndexArray arr bucket @@ -833,14 +819,14 @@         readReduceArgument param arr           | Prim _ <- paramType param = do               let i = local_tid + Imp.vi32 offset-              copyDWIM (paramName param) [] (Var arr) [i]+              copyDWIMFix (paramName param) [] (Var arr) [i]           | otherwise = do               let i = global_tid + Imp.vi32 offset-              copyDWIM (paramName param) [] (Var arr) [i]+              copyDWIMFix (paramName param) [] (Var arr) [i]          writeReduceOpResult param arr           | Prim _ <- paramType param =-              copyDWIM arr [local_tid] (Var $ paramName param) []+              copyDWIMFix arr [local_tid] (Var $ paramName param) []           | otherwise =               return () @@ -851,7 +837,7 @@           -> [VName]           -> ImpM ExplicitMemory Imp.KernelOp () groupScan constants seg_flag w lam arrs = do-  when (any (not . primType . paramType) $ lambdaParams lam) $+  unless (all (primType . paramType) $ lambdaParams lam) $     compilerLimitationS "Cannot compile parallel scans with array element type."    renamed_lam <- renameLambda lam@@ -1066,9 +1052,13 @@             dPrimV_ v $ flatf constants             f constants -sKernel :: Operations ExplicitMemory Imp.KernelOp-        -> KernelConstants -> Name -> ImpM ExplicitMemory Imp.KernelOp a -> CallKernelGen ()-sKernel ops constants name m = do+sKernelFailureTolerant :: Bool+                       -> Operations ExplicitMemory Imp.KernelOp+                       -> KernelConstants+                       -> Name+                       -> ImpM ExplicitMemory Imp.KernelOp a+                       -> CallKernelGen ()+sKernelFailureTolerant tol ops constants name m = do   body <- makeAllMemoryGlobal $ subImpM_ ops m   uses <- computeKernelUses body mempty   emit $ Imp.Op $ Imp.CallKernel Imp.Kernel@@ -1077,8 +1067,13 @@     , Imp.kernelNumGroups = [kernelNumGroups constants]     , Imp.kernelGroupSize = [kernelGroupSize constants]     , Imp.kernelName = name+    , Imp.kernelFailureTolerant = tol     } +sKernel :: Operations ExplicitMemory Imp.KernelOp+        -> KernelConstants -> Name -> ImpM ExplicitMemory Imp.KernelOp a -> CallKernelGen ()+sKernel = sKernelFailureTolerant False+ -- | A kernel with the given number of threads, running per-thread code. sKernelSimple :: String -> Imp.Exp               -> (KernelConstants -> InKernelGen ())@@ -1092,16 +1087,16 @@     f constants  copyInGroup :: CopyCompiler ExplicitMemory Imp.KernelOp-copyInGroup pt destloc srcloc n = do+copyInGroup pt destloc srcloc = do   dest_space <- entryMemSpace <$> lookupMemory (memLocationName destloc)   src_space <- entryMemSpace <$> lookupMemory (memLocationName srcloc)    if isScalarMem dest_space && isScalarMem src_space     then memLocationName destloc <-- Imp.var (memLocationName srcloc) pt-    else copyElementWise pt destloc srcloc n+    else copyElementWise pt destloc srcloc -  where isScalarMem (Space space) = space `M.member` allScalarMemory-        isScalarMem DefaultSpace = False+  where isScalarMem ScalarSpace{} = True+        isScalarMem _ = False  threadOperations, groupOperations :: KernelConstants                                   -> Operations ExplicitMemory Imp.KernelOp@@ -1138,10 +1133,10 @@       name = nameFromString $ "replicate_" ++              show (baseTag $ kernelGlobalThreadIdVar constants) -  sKernel (threadOperations constants) constants name $ do+  sKernelFailureTolerant True (threadOperations constants) constants name $ do     set_constants     sWhen (kernelThreadActive constants) $-      copyDWIM arr is' se $ drop (length ds) is'+      copyDWIMFix arr is' se $ drop (length ds) is'  replicateFunction :: PrimType -> CallKernelGen Imp.Function replicateFunction bt = do@@ -1183,7 +1178,7 @@           fname <- replicateForType v_t'           emit $ Imp.Call [] fname             [Imp.MemArg arr_mem,-             Imp.ExpArg $ unCount $ product $ map dimSizeToExp arr_shape,+             Imp.ExpArg $ product $ map (toExp' int32) arr_shape,              Imp.ExpArg $ toExp' v_t' v]     _ -> return Nothing @@ -1208,7 +1203,7 @@   let name = nameFromString $ "iota_" ++              show (baseTag $ kernelGlobalThreadIdVar constants) -  sKernel (threadOperations constants) constants name $ do+  sKernelFailureTolerant True (threadOperations constants) constants name $ do     set_constants     let gtid = kernelGlobalThreadId constants     sWhen (kernelThreadActive constants) $ do@@ -1221,28 +1216,26 @@ sCopy :: PrimType       -> MemLocation       -> MemLocation-      -> Imp.Count Imp.Elements Imp.Exp       -> CallKernelGen () sCopy bt   destloc@(MemLocation destmem _ _)   srcloc@(MemLocation srcmem srcshape _)-  n = do+  = do   -- Note that the shape of the destination and the source are   -- necessarily the same.-  let shape = map dimSizeToExp srcshape-      shape_se = map (Imp.unCount . dimSizeToExp) srcshape-      kernel_size = Imp.unCount $ n * product (drop 1 shape)+  let shape = map (toExp' int32) srcshape+      kernel_size = product shape    (constants, set_constants) <- simpleKernelConstants kernel_size "copy"    let name = nameFromString $ "copy_" ++              show (baseTag $ kernelGlobalThreadIdVar constants) -  sKernel (threadOperations constants) constants name $ do+  sKernelFailureTolerant True (threadOperations constants) constants name $ do     set_constants      let gtid = kernelGlobalThreadId constants-        dest_is = unflattenIndex shape_se gtid+        dest_is = unflattenIndex shape gtid         src_is = dest_is      (_, destspace, destidx) <- fullyIndexArray' destloc dest_is@@ -1257,21 +1250,18 @@                    -> InKernelGen ()  compileGroupResult _ constants pe (TileReturns [(w,per_group_elems)] what) = do-  dest_loc <- entryArrayLocation <$> lookupArray (patElemName pe)-  let dest_loc_offset = offsetArray dest_loc offset-      dest' = arrayDestination dest_loc_offset   n <- toExp . arraySize 0 =<< lookupType what    -- Avoid loop for the common case where each thread is statically   -- known to write at most one element.   if toExp' int32 per_group_elems == kernelGroupSize constants     then sWhen (offset + ltid .<. toExp' int32 w) $-         copyDWIMDest dest' [ltid] (Var what) [ltid]+         copyDWIMFix (patElemName pe) [ltid + offset] (Var what) [ltid]     else     sFor "i" (n `quotRoundingUp` kernelGroupSize constants) $ \i -> do       j <- fmap Imp.vi32 $ dPrimV "j" $            kernelGroupSize constants * i + ltid-      sWhen (j .<. n) $ copyDWIMDest dest' [j] (Var what) [j]+      sWhen (j .<. n) $ copyDWIMFix (patElemName pe) [j + offset] (Var what) [j]   where ltid = kernelLocalThreadId constants         offset = toExp' int32 per_group_elems * kernelGroupId constants @@ -1283,7 +1273,7 @@   is_for_thread <- mapM (dPrimV "thread_out_index") $ zipWith (+) group_is local_is    sWhen (isActive $ zip is_for_thread $ map fst dims) $-    copyDWIM (patElemName pe) (map Imp.vi32 is_for_thread) (Var what) local_is+    copyDWIMFix (patElemName pe) (map Imp.vi32 is_for_thread) (Var what) local_is  compileGroupResult space constants pe (Returns _ what) = do   in_local_memory <- arrayInLocalMemory what@@ -1291,7 +1281,7 @@    if not in_local_memory then     sWhen (kernelLocalThreadId constants .==. 0) $-    copyDWIM (patElemName pe) gids what []+    copyDWIMFix (patElemName pe) gids what []     else       -- If the result of the group is an array in local memory, we       -- store it by collective copying among all the threads of the@@ -1311,22 +1301,16 @@  compileThreadResult space _ pe (Returns _ what) = do   let is = map (Imp.vi32 . fst) $ unSegSpace space-  copyDWIM (patElemName pe) is what []+  copyDWIMFix (patElemName pe) is what []  compileThreadResult _ constants pe (ConcatReturns SplitContiguous _ per_thread_elems what) = do-  dest_loc <- entryArrayLocation <$> lookupArray (patElemName pe)-  let dest_loc_offset = offsetArray dest_loc offset-      dest' = arrayDestination dest_loc_offset-  copyDWIMDest dest' [] (Var what) []+  n <- toExp' int32 . arraySize 0 <$> lookupType what+  copyDWIM (patElemName pe) [DimSlice offset n 1] (Var what) []   where offset = toExp' int32 per_thread_elems * kernelGlobalThreadId constants  compileThreadResult _ constants pe (ConcatReturns (SplitStrided stride) _ _ what) = do-  dest_loc <- entryArrayLocation <$> lookupArray (patElemName pe)-  let dest_loc' = strideArray-                  (offsetArray dest_loc offset) $-                  toExp' int32 stride-      dest' = arrayDestination dest_loc'-  copyDWIMDest dest' [] (Var what) []+  n <- toExp' int32 . arraySize 0 <$> lookupType what+  copyDWIM (patElemName pe) [DimSlice offset n $ toExp' int32 stride] (Var what) []   where offset = kernelGlobalThreadId constants  compileThreadResult _ constants pe (WriteReturns rws _arr dests) = do@@ -1336,7 +1320,7 @@     let condInBounds i rw = 0 .<=. i .&&. i .<. rw         write = foldl (.&&.) (kernelThreadActive constants) $                 zipWith condInBounds is' rws'-    sWhen write $ copyDWIM (patElemName pe) (map (toExp' int32) is) e []+    sWhen write $ copyDWIMFix (patElemName pe) (map (toExp' int32) is) e []  compileThreadResult _ _ _ TileReturns{} =   compilerBugS "compileThreadResult: TileReturns unhandled."
src/Futhark/CodeGen/ImpGen/Kernels/SegHist.hs view
@@ -206,10 +206,10 @@   hist_el_size <- dPrimVE "hist_el_size" $ sum $ map slugElAvgSize slugs    hist_C_max <- dPrimVE "hist_C_max" $-    Imp.BinOpExp (SMin Int32) hist_T $ hist_H `quot` hist_k_ct_min+    Imp.BinOpExp (FMin Float64) (r64 hist_T) $ r64 hist_H / hist_k_ct_min    hist_M_min <- dPrimVE "hist_M_min" $-    Imp.BinOpExp (SMax Int32) 1 $ hist_T `quot` hist_C_max+    Imp.BinOpExp (SMax Int32) 1 $ t64 $ r64 hist_T / hist_C_max    -- Querying L2 cache size is not reliable.  Instead we provide a   -- tunable knob with a hopefully sane default.@@ -346,8 +346,7 @@                      -> [SegHistSlug]                      -> KernelBody ExplicitMemory                      -> [[Imp.Exp] -> ImpM ExplicitMemory Imp.KernelOp ()]-                     -> Imp.Exp-                     -> Imp.Exp+                     -> Imp.Exp -> Imp.Exp                      -> ImpM ExplicitMemory Imp.HostOp () histKernelGlobalPass map_pes num_groups group_size space slugs kbody histograms hist_S chk_i = do @@ -390,7 +389,7 @@          sComment "save map-out results" $           forM_ (zip map_pes map_res) $ \(pe, res) ->-          copyDWIM (patElemName pe)+          copyDWIMFix (patElemName pe)           (map (Imp.vi32 . fst) $ unSegSpace space)           (kernelResultSubExp res) [] @@ -416,7 +415,7 @@               dLParams $ lambdaParams lam               sLoopNest shape $ \is -> do                 forM_ (zip vs_params vs') $ \(p, res) ->-                  copyDWIM (paramName p) [] (kernelResultSubExp res) is+                  copyDWIMFix (paramName p) [] (kernelResultSubExp res) is                 do_op (bucket_is ++ is)  @@ -442,23 +441,23 @@     histKernelGlobalPass map_pes num_groups' group_size' space slugs kbody     histograms hist_S chk_i +type InitLocalHistograms = [([VName],+                              (KernelConstants, SubExp) ->+                              InKernelGen ([VName],+                                            [Imp.Exp] -> InKernelGen ()))]+ prepareIntermediateArraysLocal :: VName-                               -> Count NumGroups SubExp+                               -> Count NumGroups Imp.Exp                                -> SegSpace -> [SegHistSlug]-                               -> CallKernelGen-                                  [([VName],-                                    KernelConstants ->-                                    InKernelGen ([VName],-                                                 [Imp.Exp] -> InKernelGen ()))]-prepareIntermediateArraysLocal num_subhistos_per_group num_groups space =-  mapM onOp+                               -> CallKernelGen InitLocalHistograms+prepareIntermediateArraysLocal num_subhistos_per_group groups_per_segment space slugs = do+  num_segments <- dPrimVE "num_segments" $+                  product $ map (toExp' int32 . snd) $ init $ unSegSpace space+  mapM (onOp num_segments) slugs   where-    onOp (SegHistSlug op num_subhistos subhisto_info do_op) = do+    onOp num_segments (SegHistSlug op num_subhistos subhisto_info do_op) = do -      -- For the segmented case we produce a single histogram per group.-      if length (unSegSpace space) > 1-        then num_subhistos <-- 1-        else num_subhistos <-- toExp' int32 (unCount num_groups)+      num_subhistos <-- unCount groups_per_segment * num_segments        emit $ Imp.DebugPrint "Number of subhistograms in global memory" $         Just $ Imp.vi32 num_subhistos@@ -467,35 +466,34 @@         case do_op of           AtomicPrim f -> return $ const $ return f           AtomicCAS f -> return $ const $ return f-          AtomicLocking f -> do-+          AtomicLocking f -> return $ \(constants, hist_H_chk) -> do             let lock_shape =                   Shape $ Var num_subhistos_per_group :                   shapeDims (histShape op) ++-                  [histWidth op]+                  [hist_H_chk]              dims <- mapM toExp $ shapeDims lock_shape -            return $ \constants -> do-              locks <- sAllocArray "locks" int32 lock_shape $ Space "local"+            locks <- sAllocArray "locks" int32 lock_shape $ Space "local" -              sComment "All locks start out unlocked" $-                groupCoverSpace constants dims $ \is ->-                copyDWIM locks is (intConst Int32 0) []+            sComment "All locks start out unlocked" $+              groupCoverSpace constants dims $ \is ->+              copyDWIMFix locks is (intConst Int32 0) [] -              return $ f $ Locking locks 0 1 0 id+            return $ f $ Locking locks 0 1 0 id        -- Initialise local-memory sub-histograms.  These are       -- represented as two-dimensional arrays.-      let init_local_subhistos constants = do+      let init_local_subhistos (constants, hist_H_chk) = do             local_subhistos <-               forM (histType op) $ \t -> do                 let sub_local_shape =-                      Shape [Var num_subhistos_per_group] <> arrayShape t+                      Shape [Var num_subhistos_per_group] <>+                      (arrayShape t `setOuterDim` hist_H_chk)                 sAllocArray "subhistogram_local"                   (elemType t) sub_local_shape (Space "local") -            do_op' <- mk_op constants+            do_op' <- mk_op (constants, hist_H_chk)              return (local_subhistos, do_op' (Space "local") local_subhistos) @@ -506,16 +504,16 @@        return (glob_subhistos, init_local_subhistos) -histKernelLocal :: VName -> Count NumGroups Imp.Exp-                  -> [PatElem ExplicitMemory]-                  -> Count NumGroups SubExp -> Count GroupSize SubExp-                  -> SegSpace-                  -> [SegHistSlug]-                  -> KernelBody ExplicitMemory-                  -> CallKernelGen ()-histKernelLocal num_subhistos_per_group_var groups_per_segment map_pes num_groups group_size space slugs kbody = do-  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size+histKernelLocalPass :: VName -> Count NumGroups Imp.Exp+                    -> [PatElem ExplicitMemory]+                    -> Count NumGroups Imp.Exp -> Count GroupSize Imp.Exp+                    -> SegSpace+                    -> [SegHistSlug]+                    -> KernelBody ExplicitMemory+                    -> InitLocalHistograms -> Imp.Exp -> Imp.Exp+                    -> CallKernelGen ()+histKernelLocalPass num_subhistos_per_group_var groups_per_segment map_pes num_groups group_size space slugs kbody+                    init_histograms hist_S chk_i = do   let (space_is, space_sizes) = unzip $ unSegSpace space       segment_is = init space_is       segment_dims = init space_sizes@@ -524,15 +522,14 @@    segment_size' <- toExp segment_size -  emit $ Imp.DebugPrint "Number of local subhistograms per group" $ Just num_subhistos_per_group--  init_histograms <--    prepareIntermediateArraysLocal num_subhistos_per_group_var num_groups space slugs-   num_segments <- dPrimVE "num_segments" $                   product $ map (toExp' int32) segment_dims -  sKernelThread "seghist_local" num_groups' group_size' (segFlat space) $ \constants ->+  hist_H_chks <- forM (map (histWidth . slugOp) slugs) $ \w -> do+    w' <- toExp w+    dPrimV "hist_H_chk" $ w' `quotRoundingUp` hist_S++  sKernelThread "seghist_local" num_groups group_size (segFlat space) $ \constants ->     virtualiseGroups constants SegVirt (unCount groups_per_segment * num_segments) $ \group_id_var -> do      let group_id = Imp.vi32 group_id_var@@ -550,10 +547,10 @@     zipWithM_ dPrimV_ segment_is $       unflattenIndex (map (toExp' int32) segment_dims) flat_segment_id -    histograms <- forM init_histograms $-                  \(glob_subhistos, init_local_subhistos) -> do-      (local_subhistos, do_op) <- init_local_subhistos constants-      return (zip glob_subhistos local_subhistos, do_op)+    histograms <- forM (zip init_histograms hist_H_chks) $+                  \((glob_subhistos, init_local_subhistos), hist_H_chk) -> do+      (local_subhistos, do_op) <- init_local_subhistos (constants, Var hist_H_chk)+      return (zip glob_subhistos local_subhistos, hist_H_chk, do_op)      -- Find index of local subhistograms updated by this thread.  We     -- try to ensure, as much as possible, that threads in the same@@ -562,19 +559,14 @@       dPrimVE "thread_local_subhisto_i" $       kernelLocalThreadId constants `rem` num_subhistos_per_group -    let (red_res, map_res) = splitFromEnd (length map_pes) $-                             map kernelResultSubExp $ kernelBodyResult kbody-        (buckets, vs) = splitAt (length slugs) red_res-        perOp = chunks $ map (length . histDest . slugOp) slugs--    let onSlugs f = forM_ (zip slugs histograms) $ \(slug, (dests, _)) -> do-          let histo_dims = map (toExp' int32) $ histWidth (slugOp slug) :+    let onSlugs f = forM_ (zip slugs histograms) $ \(slug, (dests, hist_H_chk, _)) -> do+          let histo_dims = fmap (toExp' int32) $ Var hist_H_chk :                            shapeDims (histShape (slugOp slug))           histo_size <- dPrimVE "histo_size" $ product histo_dims-          f slug dests histo_dims histo_size+          f slug dests (Imp.vi32 hist_H_chk) histo_dims histo_size      let onAllHistograms f =-          onSlugs $ \slug dests histo_dims histo_size -> do+          onSlugs $ \slug dests hist_H_chk histo_dims histo_size -> do             let group_hists_size = num_subhistos_per_group * histo_size             init_per_thread <- dPrimVE "init_per_thread" $                                group_hists_size `quotRoundingUp`@@ -590,23 +582,25 @@                               num_subhistos_per_group * histo_size * gid_in_segment + j                    local_subhisto_i <- dPrimVE "local_subhisto_i" $ j `quot` histo_size-                  let bucket_is = unflattenIndex histo_dims $ j `rem` histo_size+                  let local_bucket_is = unflattenIndex histo_dims $ j `rem` histo_size+                      global_bucket_is = head local_bucket_is + chk_i * hist_H_chk :+                                         tail local_bucket_is                   global_subhisto_i <- dPrimVE "global_subhisto_i" $ j_offset `quot` histo_size                    sWhen (j .<. group_hists_size) $                     f dest_local dest_global (slugOp slug) ne                     local_subhisto_i global_subhisto_i-                    bucket_is+                    local_bucket_is global_bucket_is      sComment "initialize histograms in local memory" $-      onAllHistograms $ \dest_local dest_global op ne local_subhisto_i global_subhisto_i bucket_is ->+      onAllHistograms $ \dest_local dest_global op ne local_subhisto_i global_subhisto_i local_bucket_is global_bucket_is ->       sComment "First subhistogram is initialised from global memory; others with neutral element." $ do-      let global_is = map Imp.vi32 segment_is ++ [0] ++ bucket_is-          local_is = local_subhisto_i : bucket_is+      let global_is = map Imp.vi32 segment_is ++ [0] ++ global_bucket_is+          local_is = local_subhisto_i : local_bucket_is       sIf (global_subhisto_i .==. 0)-        (copyDWIM dest_local local_is (Var dest_global) global_is)+        (copyDWIMFix dest_local local_is (Var dest_global) global_is)         (sLoopNest (histShape op) $ \is ->-            copyDWIM dest_local (local_is++is) ne [])+            copyDWIMFix dest_local (local_is++is) ne [])      sOp Imp.LocalBarrier @@ -614,23 +608,33 @@       dPrimV_ i_in_segment ie        -- We execute the bucket function once and update each histogram-      -- serially.  This also involves writing to the mapout arrays.+      -- serially.  This also involves writing to the mapout arrays if+      -- this is the first chunk.        compileStms mempty (kernelBodyStms kbody) $ do -        sComment "save map-out results" $+        let (red_res, map_res) = splitFromEnd (length map_pes) $+                             map kernelResultSubExp $ kernelBodyResult kbody+            (buckets, vs) = splitAt (length slugs) red_res+            perOp = chunks $ map (length . histDest . slugOp) slugs++        sWhen (chk_i .==. 0) $+          sComment "save map-out results" $           forM_ (zip map_pes map_res) $ \(pe, se) ->-          copyDWIM (patElemName pe)+          copyDWIMFix (patElemName pe)           (map Imp.vi32 space_is) se []          forM_ (zip4 (map slugOp slugs) histograms buckets (perOp vs)) $           \(HistOp dest_w _ _ _ shape lam,-            (_, do_op), bucket, vs') -> do+            (_, hist_H_chk, do_op), bucket, vs') -> do -            let bucket' = toExp' int32 bucket+            let chk_beg = chk_i * Imp.vi32 hist_H_chk+                bucket' = toExp' int32 bucket                 dest_w' = toExp' int32 dest_w-                bucket_in_bounds = 0 .<=. bucket' .&&. bucket' .<. dest_w'-                bucket_is = [thread_local_subhisto_i, bucket']+                bucket_in_bounds = bucket' .<. dest_w' .&&.+                                   chk_beg .<=. bucket' .&&.+                                   bucket' .<. (chk_beg + Imp.vi32 hist_H_chk)+                bucket_is = [thread_local_subhisto_i, bucket' - chk_beg]                 vs_params = takeLast (length vs') $ lambdaParams lam              sComment "perform atomic updates" $@@ -638,50 +642,93 @@               dLParams $ lambdaParams lam               sLoopNest shape $ \is -> do                 forM_ (zip vs_params vs') $ \(p, v) ->-                  copyDWIM (paramName p) [] v is+                  copyDWIMFix (paramName p) [] v is                 do_op (bucket_is ++ is) -    sOp Imp.LocalBarrier+    sOp Imp.ErrorSync     sOp Imp.GlobalBarrier      sComment "Compact the multiple local memory subhistograms to result in global memory" $-      onSlugs $ \slug dests histo_dims histo_size -> do+      onSlugs $ \slug dests hist_H_chk histo_dims histo_size -> do       bins_per_thread <- dPrimVE "init_per_thread" $                          histo_size `quotRoundingUp` kernelGroupSize constants +      trunc_H <- dPrimV "trunc_H" $+                 Imp.BinOpExp (SMin Int32) hist_H_chk $+                 toExp' int32 (histWidth (slugOp slug)) -+                 chk_i * head histo_dims+      let trunc_histo_dims = map (toExp' int32) $ Var trunc_H :+                             shapeDims (histShape (slugOp slug))+      trunc_histo_size <- dPrimVE "histo_size" $ product trunc_histo_dims+       sFor "local_i" bins_per_thread $ \i -> do         j <- dPrimVE "j" $              i * kernelGroupSize constants + kernelLocalThreadId constants-        sWhen (j .<. histo_size) $ do+        sWhen (j .<. trunc_histo_size) $ do           -- We are responsible for compacting the flat bin 'j', which           -- we immediately unflatten.-          let bucket_is = unflattenIndex histo_dims j+          let local_bucket_is = unflattenIndex histo_dims j+              global_bucket_is = head local_bucket_is + chk_i * hist_H_chk :+                                 tail local_bucket_is           dLParams $ lambdaParams $ histOp $ slugOp slug-          let (xparams, yparams) = splitAt (length local_dests) $+          let (global_dests, local_dests) = unzip dests+              (xparams, yparams) = splitAt (length local_dests) $                                    lambdaParams $ histOp $ slugOp slug-              (global_dests, local_dests) = unzip dests            sComment "Read values from subhistogram 0." $             forM_ (zip xparams local_dests) $ \(xp, subhisto) ->-            copyDWIM+            copyDWIMFix             (paramName xp) []-            (Var subhisto) (0:bucket_is)+            (Var subhisto) (0:local_bucket_is)            sComment "Accumulate based on values in other subhistograms." $             sFor "subhisto_id" (num_subhistos_per_group - 1) $ \subhisto_id -> do               forM_ (zip yparams local_dests) $ \(yp, subhisto) ->-                copyDWIM+                copyDWIMFix                 (paramName yp) []-                (Var subhisto) (subhisto_id + 1 : bucket_is)+                (Var subhisto) (subhisto_id + 1 : local_bucket_is)               compileBody' xparams $ lambdaBody $ histOp $ slugOp slug            sComment "Put final bucket value in global memory." $ do-            let global_is = map Imp.vi32 segment_is ++-                            [group_id `rem` unCount groups_per_segment] ++-                            bucket_is+            let global_is =+                  map Imp.vi32 segment_is +++                  [group_id `rem` unCount groups_per_segment] +++                  global_bucket_is             forM_ (zip xparams global_dests) $ \(xp, global_dest) ->-              copyDWIM global_dest global_is (Var $ paramName xp) []+              copyDWIMFix global_dest global_is (Var $ paramName xp) [] +histKernelLocal :: VName -> Count NumGroups Imp.Exp+                -> [PatElem ExplicitMemory]+                -> Count NumGroups SubExp -> Count GroupSize SubExp+                -> SegSpace+                -> Imp.Exp+                -> [SegHistSlug]+                -> KernelBody ExplicitMemory+                -> CallKernelGen ()+histKernelLocal num_subhistos_per_group_var groups_per_segment map_pes num_groups group_size space hist_S slugs kbody = do+  num_groups' <- traverse toExp num_groups+  group_size' <- traverse toExp group_size+  let num_subhistos_per_group = Imp.var num_subhistos_per_group_var int32++  emit $ Imp.DebugPrint "Number of local subhistograms per group" $ Just num_subhistos_per_group++  init_histograms <-+    prepareIntermediateArraysLocal num_subhistos_per_group_var groups_per_segment space slugs++  sFor "chk_i" hist_S $ \chk_i ->+    histKernelLocalPass+    num_subhistos_per_group_var groups_per_segment map_pes num_groups' group_size' space slugs kbody+    init_histograms hist_S chk_i++-- | The maximum number of passes we are willing to accept for this+-- kind of atomic update.+slugMaxLocalMemPasses :: SegHistSlug -> Int+slugMaxLocalMemPasses slug =+  case slugAtomicUpdate slug of+    AtomicPrim _ -> 2+    AtomicCAS _  -> 4+    AtomicLocking _ -> 5+ localMemoryCase :: [PatElem ExplicitMemory]                 -> Imp.Exp                 -> SegSpace@@ -707,6 +754,8 @@    let r64 = ConvOpExp (SIToFP Int32 Float64)       t64 = ConvOpExp (FPToSI Float64 Int32)+      i32_to_i64 = ConvOpExp (SExt Int32 Int64)+      i64_to_i32 = ConvOpExp (SExt Int64 Int32)       f64ceil x = t64 $ FunExp "round64" [x] $ FloatType Float64    -- M approximation.@@ -764,8 +813,8 @@   -- Minimal sequential chunking factor.   let q_small = 2 -  hist_Nout <- dPrimVE "hist_Nout" $-               product $ map (toExp' int32) segment_dims+  -- The number of segments/histograms produced..+  hist_Nout <- dPrimVE "hist_Nout" $ product $ map (toExp' int32) segment_dims    hist_Nin <- dPrimVE "hist_Nin" $ toExp' int32 $ last space_sizes @@ -774,9 +823,11 @@     if segmented then do        hist_T_hist_min <- dPrimVE "hist_T_hist_min" $-                         Imp.BinOpExp (SMin Int32) (hist_Nin * hist_Nout) hist_T+                         i64_to_i32 $+                         Imp.BinOpExp (SMin Int64)+                         (i32_to_i64 hist_Nin * i32_to_i64 hist_Nout) (i32_to_i64 hist_T)                          `quotRoundingUp`-                         hist_Nout+                         i32_to_i64 hist_Nout        -- Number of groups, rounded up.       let r = hist_T_hist_min `quotRoundingUp` hist_B@@ -809,19 +860,29 @@   emit $ Imp.DebugPrint "local memory needed" $     Just $ hist_H * hist_el_size * Imp.vi32 hist_M +  -- local_mem_needed is what we need to keep a single bucket in local+  -- memory - this is an absolute minimum.  We can fit anything else+  -- by doing multiple passes, although more than a few is+  -- (heuristically) not efficient.+  local_mem_needed <- dPrimVE "local_mem_needed" $ hist_el_size * Imp.vi32 hist_M+  hist_S <- dPrimVE "hist_S" $ (hist_H * local_mem_needed) `quotRoundingUp` Imp.vi32 hist_L+  let max_S = case bodyPassage kbody of+                MustBeSinglePass -> 1+                MayBeMultiPass -> fromIntegral $ maximum $ map slugMaxLocalMemPasses slugs+   -- We only use local memory if the number of updates per histogram   -- at least matches the histogram size, as otherwise it is not   -- asymptotically efficient.  This mostly matters for the segmented   -- case.   let pick_local =         hist_Nin .>=. hist_H-        .&&. (hist_H * hist_el_size * Imp.vi32 hist_M-              .<=. Imp.vi32 hist_L)+        .&&. (local_mem_needed .<=. Imp.vi32 hist_L)+        .&&. (hist_S .<=. max_S)         .&&. hist_C .<=. hist_B         .&&. Imp.vi32 hist_M .>. 0        groups_per_segment-        | segmented = 1+        | segmented = num_groups' `quotRoundingUp` Imp.Count hist_Nout         | otherwise = num_groups'        run = do@@ -829,8 +890,11 @@         emit $ Imp.DebugPrint "Histogram size (H)" $ Just hist_H         emit $ Imp.DebugPrint "Multiplication degree (M)" $ Just $ Imp.vi32 hist_M         emit $ Imp.DebugPrint "Cooperation level (C)" $ Just hist_C+        emit $ Imp.DebugPrint "Number of chunks (S)" $ Just hist_S+        when segmented $+          emit $ Imp.DebugPrint "Groups per segment" $ Just $ unCount groups_per_segment         histKernelLocal hist_M groups_per_segment map_pes-          num_groups group_size space slugs kbody+          num_groups group_size space hist_S slugs kbody    return (pick_local, run) @@ -858,43 +922,45 @@   h <- dPrimVE "h" $ Imp.unCount $ sum op_hs   seg_h <- dPrimVE "seg_h" $ Imp.unCount $ sum op_seg_hs -  -- Maximum group size (or actual, in this case).-  let hist_B = unCount group_size'+  -- Check for emptyness to avoid division-by-zero.+  sUnless (seg_h .==. 0) $ do -  -- Size of a histogram.-  hist_H <- dPrimVE "hist_H" $ sum $ map (toExp' int32 . histWidth) ops+    -- Maximum group size (or actual, in this case).+    let hist_B = unCount group_size' -  -- Size of a single histogram element.  Actually the weighted-  -- average of histogram elements in cases where we have more than-  -- one histogram operation, plus any locks.-  let lockSize slug = case slugAtomicUpdate slug of-                        AtomicLocking{} -> Just $ primByteSize int32-                        _               -> Nothing-  hist_el_size <- dPrimVE "hist_el_size" $ foldl' (+) (h `quotRoundingUp` hist_H) $-                  mapMaybe lockSize slugs+    -- Size of a histogram.+    hist_H <- dPrimVE "hist_H" $ sum $ map (toExp' int32 . histWidth) ops -  -- Input elements contributing to each histogram.-  hist_N <- dPrimVE "hist_N" segment_size+    -- Size of a single histogram element.  Actually the weighted+    -- average of histogram elements in cases where we have more than+    -- one histogram operation, plus any locks.+    let lockSize slug = case slugAtomicUpdate slug of+                          AtomicLocking{} -> Just $ primByteSize int32+                          _               -> Nothing+    hist_el_size <- dPrimVE "hist_el_size" $ foldl' (+) (h `quotRoundingUp` hist_H) $+                    mapMaybe lockSize slugs -  -- Compute RF as the average RF over all the histograms.-  hist_RF <- dPrimVE "hist_RF" $-             sum (map (toExp' int32. histRaceFactor . slugOp) slugs)-             `quot`-             genericLength slugs+    -- Input elements contributing to each histogram.+    hist_N <- dPrimVE "hist_N" segment_size -  -- Check for emptyness to avoid division-by-zero.-  sUnless (seg_h .==. 0) $ do+    -- Compute RF as the average RF over all the histograms.+    hist_RF <- dPrimVE "hist_RF" $+               sum (map (toExp' int32. histRaceFactor . slugOp) slugs)+               `quot`+               genericLength slugs      let hist_T = unCount num_groups' * unCount group_size'     emit $ Imp.DebugPrint "\n# SegHist" Nothing     emit $ Imp.DebugPrint "Number of threads (T)" $ Just hist_T-    emit $ Imp.DebugPrint "Memory per set of subhistograms per segment" $ Just h-    emit $ Imp.DebugPrint "Memory per set of subhistograms times segments" $ Just seg_h-    emit $ Imp.DebugPrint "Input elements per histogram (N)" $ Just hist_N     emit $ Imp.DebugPrint "Desired group size (B)" $ Just hist_B     emit $ Imp.DebugPrint "Histogram size (H)" $ Just hist_H+    emit $ Imp.DebugPrint "Input elements per histogram (N)" $ Just hist_N+    emit $ Imp.DebugPrint "Number of segments" $+      Just $ product $ map (toExp' int32 . snd) segment_dims     emit $ Imp.DebugPrint "Histogram element size (el_size)" $ Just hist_el_size     emit $ Imp.DebugPrint "Race factor (RF)" $ Just hist_RF+    emit $ Imp.DebugPrint "Memory per set of subhistograms per segment" $ Just h+    emit $ Imp.DebugPrint "Memory per set of subhistograms times segments" $ Just seg_h      (use_local_memory, run_in_local_memory) <-       localMemoryCase map_pes hist_T space hist_H hist_el_size hist_N hist_RF slugs kbody
src/Futhark/CodeGen/ImpGen/Kernels/SegMap.hs view
@@ -21,9 +21,6 @@               -> KernelBody ExplicitMemory               -> CallKernelGen () -compileSegMap _ SegThreadScalar{} _ _ =-  fail "compileSegMap: SegThreadScalar cannot be compiled at top level."- compileSegMap pat lvl space kbody = do   let (is, dims) = unzip $ unSegSpace space   dims' <- mapM toExp dims@@ -32,8 +29,6 @@   group_size' <- traverse toExp $ segGroupSize lvl    case lvl of-    SegThreadScalar{} ->-      fail "compileSegMap: SegThreadScalar cannot be compiled at top level."      SegThread{} ->       sKernelThread "segmap" num_groups' group_size' (segFlat space) $ \constants -> do
src/Futhark/CodeGen/ImpGen/Kernels/SegRed.hs view
@@ -257,21 +257,21 @@       let out_of_bounds =             forM_ (zip reds reds_arrs) $ \(SegRedOp _ _ nes _, red_arrs) ->             forM_ (zip red_arrs nes) $ \(arr, ne) ->-            copyDWIM arr [ltid] ne []+            copyDWIMFix arr [ltid] ne []            in_bounds =             body constants $ \red_res ->             sComment "save results to be reduced" $ do             let red_dests = zip (concat reds_arrs) $ repeat [ltid]             forM_ (zip red_dests red_res) $ \((d,d_is), (res, res_is)) ->-              copyDWIM d d_is res res_is+              copyDWIMFix d d_is res res_is        sComment "apply map function if in bounds" $         sIf (segment_size .>. 0 .&&.              isActive (init $ zip gtids dims) .&&.              ltid .<. segment_size * segments_per_group) in_bounds out_of_bounds -      sOp Imp.LocalBarrier+      sOp Imp.ErrorSync -- Also implicitly barrier.        let crossesSegment from to = (to-from) .>. (to `rem` segment_size)       sWhen (segment_size .>. 0) $@@ -288,7 +288,7 @@         -- Figure out which segment result this thread should write...         let flat_segment_index = group_id' * segments_per_group + ltid             gtids' = unflattenIndex (init dims') flat_segment_index-        copyDWIM (patElemName pe) gtids'+        copyDWIMFix (patElemName pe) gtids'                         (Var arr) [(ltid+1) * segment_size_nonzero - 1]        -- Finally another barrier, because we will be writing to the@@ -398,7 +398,7 @@             forM_ (zip slugs segred_pes) $ \(slug, pes) ->             sWhen (local_tid .==. 0) $               forM_ (zip pes (slugAccs slug)) $ \(v, (acc, acc_is)) ->-              copyDWIM (patElemName v) (map (`Imp.var` int32) segment_gtids) (Var acc) acc_is+              copyDWIMFix (patElemName v) (map (`Imp.var` int32) segment_gtids) (Var acc) acc_is        sIf (groups_per_segment .==. 1) one_group_per_segment multiple_groups_per_segment @@ -483,7 +483,7 @@     forM_ slugs $ \slug ->     forM_ (zip (slugAccs slug) (slugNeutral slug)) $ \((acc, acc_is), ne) ->     sLoopNest (slugShape slug) $ \vec_is ->-    copyDWIM acc (acc_is++vec_is) ne []+    copyDWIMFix acc (acc_is++vec_is) ne []    slugs_op_renamed <- mapM (renameLambda . segRedLambda . slugOp) slugs @@ -492,13 +492,13 @@         sLoopNest (slugShape slug) $ \vec_is -> do           comment "to reduce current chunk, first store our result in memory" $ do             forM_ (zip (slugParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->-              copyDWIM (paramName p) [] (Var acc) (acc_is++vec_is)+              copyDWIMFix (paramName p) [] (Var acc) (acc_is++vec_is)              forM_ (zip (slugArrs slug) (slugParams slug)) $ \(arr, p) ->               when (primType $ paramType p) $-              copyDWIM arr [local_tid] (Var $ paramName p) []+              copyDWIMFix arr [local_tid] (Var $ paramName p) [] -          sOp Imp.LocalBarrier+          sOp Imp.ErrorSync -- Also implicitly barrier.            groupReduce constants (kernelGroupSize constants) slug_op_renamed (slugArrs slug) @@ -507,7 +507,7 @@           sComment "first thread saves the result in accumulator" $             sWhen (local_tid .==. 0) $             forM_ (zip (slugAccs slug) (lambdaParams slug_op_renamed)) $ \((acc, acc_is), p) ->-            copyDWIM acc (acc_is++vec_is) (Var $ paramName p) []+            copyDWIMFix acc (acc_is++vec_is) (Var $ paramName p) []    -- If this is a non-commutative reduction, each thread must run the   -- loop the same number of iterations, because we will be performing@@ -540,17 +540,17 @@         sLoopNest (slugShape slug) $ \vec_is -> do         sComment "load accumulator" $           forM_ (zip (accParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->-          copyDWIM (paramName p) [] (Var acc) (acc_is ++ vec_is)+          copyDWIMFix (paramName p) [] (Var acc) (acc_is ++ vec_is)         sComment "load new values" $           forM_ (zip (nextParams slug) red_res) $ \(p, (res, res_is)) ->-          copyDWIM (paramName p) [] res (res_is ++ vec_is)+          copyDWIMFix (paramName p) [] res (res_is ++ vec_is)         sComment "apply reduction operator" $           compileStms mempty (bodyStms $ slugBody slug) $           sComment "store in accumulator" $           forM_ (zip                   (slugAccs slug)                   (bodyResult $ slugBody slug)) $ \((acc, acc_is), se) ->-          copyDWIM acc (acc_is ++ vec_is) se []+          copyDWIMFix acc (acc_is ++ vec_is) se []      case comm of       Noncommutative -> do@@ -560,7 +560,7 @@                 forM_ slugs $ \slug ->                 forM_ (zip (slugAccs slug) (slugNeutral slug)) $ \((acc, acc_is), ne) ->                 sLoopNest (slugShape slug) $ \vec_is ->-                copyDWIM acc (acc_is++vec_is) ne []+                copyDWIMFix acc (acc_is++vec_is) ne []           sUnless (local_tid .==. 0) reset_to_neutral       _ -> return () @@ -583,7 +583,7 @@     Noncommutative ->       forM_ slugs $ \slug ->       forM_ (zip (accParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->-      copyDWIM (paramName p) [] (Var acc) acc_is+      copyDWIMFix (paramName p) [] (Var acc) acc_is     _ -> doTheReduction    return slugs_op_renamed@@ -613,7 +613,7 @@   comment "first thread in group saves group result to global memory" $     sWhen (local_tid .==. 0) $ do     forM_ (take (length nes) $ zip group_res_arrs (slugAccs slug)) $ \(v, (acc, acc_is)) ->-      copyDWIM v [0, group_id] (Var acc) acc_is+      copyDWIMFix v [0, group_id] (Var acc) acc_is     sOp Imp.MemFenceGlobal     -- Increment the counter, thus stating that our result is     -- available.@@ -626,7 +626,7 @@   sOp Imp.GlobalBarrier    is_last_group <- dPrim "is_last_group" Bool-  copyDWIM is_last_group [] (Var sync_arr) [0]+  copyDWIMFix is_last_group [] (Var sync_arr) [0]   sWhen (Imp.var is_last_group Bool) $ do     -- The final group has written its result (and it was     -- us!), so read in all the group results and perform the@@ -642,14 +642,14 @@         forM_ (zip4 red_acc_params red_arrs nes group_res_arrs) $         \(p, arr, ne, group_res_arr) -> do           let load_group_result =-                copyDWIM (paramName p) []+                copyDWIMFix (paramName p) []                 (Var group_res_arr) ([0, first_group_for_segment + local_tid] ++ vec_is)               load_neutral_element =-                copyDWIM (paramName p) [] ne []+                copyDWIMFix (paramName p) [] ne []           sIf (local_tid .<. groups_per_segment)             load_group_result load_neutral_element           when (primType $ paramType p) $-            copyDWIM arr [local_tid] (Var $ paramName p) []+            copyDWIMFix arr [local_tid] (Var $ paramName p) []        sOp Imp.LocalBarrier @@ -659,4 +659,4 @@         sComment "and back to memory with the final result" $           sWhen (local_tid .==. 0) $           forM_ (zip segred_pes $ lambdaParams red_op_renamed) $ \(pe, p) ->-          copyDWIM (patElemName pe) (segment_gtids++vec_is) (Var $ paramName p) []+          copyDWIMFix (patElemName pe) (segment_gtids++vec_is) (Var $ paramName p) []
src/Futhark/CodeGen/ImpGen/Kernels/SegScan.hs view
@@ -75,7 +75,7 @@           splitAt (length nes) $ lambdaParams scan_op      forM_ (zip scan_x_params nes) $ \(p, ne) ->-      copyDWIM (paramName p) [] ne []+      copyDWIMFix (paramName p) [] ne []      sFor "j" elems_per_thread $ \j -> do       chunk_offset <- dPrimV "chunk_offset" $@@ -92,13 +92,13 @@             let (scan_res, map_res) = splitAt (length nes) $ kernelBodyResult kbody             sComment "write to-scan values to parameters" $               forM_ (zip scan_y_params scan_res) $ \(p, se) ->-              copyDWIM (paramName p) [] (kernelResultSubExp se) []+              copyDWIMFix (paramName p) [] (kernelResultSubExp se) []             sComment "write mapped values results to global memory" $               forM_ (zip (drop (length nes) pes) map_res) $ \(pe, se) ->-              copyDWIM (patElemName pe) (map (`Imp.var` int32) gtids)+              copyDWIMFix (patElemName pe) (map (`Imp.var` int32) gtids)               (kernelResultSubExp se) []           when_out_of_bounds = forM_ (zip scan_y_params nes) $ \(p, ne) ->-            copyDWIM (paramName p) [] ne []+            copyDWIMFix (paramName p) [] ne []        sComment "threads in bounds read input; others get neutral element" $         sIf in_bounds when_in_bounds when_out_of_bounds@@ -106,7 +106,7 @@       sComment "combine with carry and write to local memory" $         compileStms mempty (bodyStms $ lambdaBody scan_op) $         forM_ (zip local_arrs $ bodyResult $ lambdaBody scan_op) $ \(arr, se) ->-          copyDWIM arr [kernelLocalThreadId constants] se []+          copyDWIMFix arr [kernelLocalThreadId constants] se []        let crossesSegment' = do             f <- crossesSegment@@ -115,22 +115,24 @@                   to' = to + Imp.var chunk_offset int32               in f from' to' +      sOp Imp.ErrorSync -- Also implicitly barrier.+       groupScan constants crossesSegment'         (kernelGroupSize constants) scan_op_renamed local_arrs        sComment "threads in bounds write partial scan result" $         sWhen in_bounds $ forM_ (zip pes local_arrs) $ \(pe, arr) ->-        copyDWIM (patElemName pe) (map (`Imp.var` int32) gtids)+        copyDWIMFix (patElemName pe) (map (`Imp.var` int32) gtids)         (Var arr) [kernelLocalThreadId constants]        sOp Imp.LocalBarrier        let load_carry =             forM_ (zip local_arrs scan_x_params) $ \(arr, p) ->-            copyDWIM (paramName p) [] (Var arr) [kernelGroupSize constants - 1]+            copyDWIMFix (paramName p) [] (Var arr) [kernelGroupSize constants - 1]           load_neutral =             forM_ (zip nes scan_x_params) $ \(ne, p) ->-            copyDWIM (paramName p) [] ne []+            copyDWIMFix (paramName p) [] ne []        sComment "first thread reads last element as carry-in for next iteration" $         sWhen (kernelLocalThreadId constants .==. 0) $@@ -173,10 +175,10 @@     let in_bounds =           foldl1 (.&&.) $ zipWith (.<.) (map (`Imp.var` int32) gtids) dims'         when_in_bounds = forM_ (zip local_arrs pes) $ \(arr, pe) ->-          copyDWIM arr [kernelLocalThreadId constants]+          copyDWIMFix arr [kernelLocalThreadId constants]           (Var $ patElemName pe) $ map (`Imp.var` int32) gtids         when_out_of_bounds = forM_ (zip local_arrs nes) $ \(arr, ne) ->-          copyDWIM arr [kernelLocalThreadId constants] ne []+          copyDWIMFix arr [kernelLocalThreadId constants] ne []      sComment "threads in bound read carries; others get neutral element" $       sIf in_bounds when_in_bounds when_out_of_bounds@@ -186,7 +188,7 @@      sComment "threads in bounds write scanned carries" $       sWhen in_bounds $ forM_ (zip pes local_arrs) $ \(pe, arr) ->-      copyDWIM (patElemName pe) (map (`Imp.var` int32) gtids)+      copyDWIMFix (patElemName pe) (map (`Imp.var` int32) gtids)       (Var arr) [kernelLocalThreadId constants]  scanStage3 :: Pattern ExplicitMemory@@ -226,12 +228,12 @@       let (scan_x_params, scan_y_params) =             splitAt (length nes) $ lambdaParams scan_op       forM_ (zip scan_x_params pes) $ \(p, pe) ->-        copyDWIM (paramName p) [] (Var $ patElemName pe) carry_in_idx+        copyDWIMFix (paramName p) [] (Var $ patElemName pe) carry_in_idx       forM_ (zip scan_y_params pes) $ \(p, pe) ->-        copyDWIM (paramName p) [] (Var $ patElemName pe) $ map (`Imp.var` int32) gtids+        copyDWIMFix (paramName p) [] (Var $ patElemName pe) $ map (`Imp.var` int32) gtids       compileBody' scan_x_params $ lambdaBody scan_op       forM_ (zip scan_x_params pes) $ \(p, pe) ->-        copyDWIM (patElemName pe) (map (`Imp.var` int32) gtids) (Var $ paramName p) []+        copyDWIMFix (patElemName pe) (map (`Imp.var` int32) gtids) (Var $ paramName p) []  -- | Compile 'SegScan' instance to host-level code with calls to -- various kernels.
src/Futhark/CodeGen/ImpGen/Kernels/ToOpenCL.hs view
@@ -10,7 +10,6 @@  import Control.Monad.State import Control.Monad.Identity-import Control.Monad.Writer import Control.Monad.Reader import Data.Maybe import qualified Data.Set as S@@ -28,7 +27,6 @@ import Futhark.CodeGen.ImpCode.OpenCL hiding (Program) import qualified Futhark.CodeGen.ImpCode.OpenCL as ImpOpenCL import Futhark.MonadFreshNames-import Futhark.Representation.ExplicitMemory (allScalarMemory) import Futhark.Util (zEncodeString)  kernelsToCUDA, kernelsToOpenCL :: ImpKernels.Program@@ -41,17 +39,16 @@                  -> ImpKernels.Program                  -> Either InternalError ImpOpenCL.Program translateKernels target (ImpKernels.Functions funs) = do-  (prog', ToOpenCL extra_funs kernels requirements sizes) <--    runWriterT $ fmap Functions $ forM funs $ \(fname, fun) ->+  (prog', ToOpenCL kernels used_types sizes failures) <-+    flip runStateT initialOpenCL $ fmap Functions $ forM funs $ \(fname, fun) ->     (fname,) <$> runReaderT (traverse (onHostOp target) fun) fname-  let kernel_names = M.keys kernels-      opencl_code = openClCode $ M.elems kernels-      opencl_prelude = pretty $ genPrelude target requirements-  return $ ImpOpenCL.Program opencl_code opencl_prelude kernel_names-    (S.toList $ openclUsedTypes requirements) (cleanSizes sizes) $-    ImpOpenCL.Functions (M.toList extra_funs) <> prog'+  let kernels' = M.map fst kernels+      opencl_code = openClCode $ map snd $ M.elems kernels+      opencl_prelude = pretty $ genPrelude target used_types+  return $ ImpOpenCL.Program opencl_code opencl_prelude kernels'+    (S.toList used_types) (cleanSizes sizes) failures prog'   where genPrelude TargetOpenCL = genOpenClPrelude-        genPrelude TargetCUDA = genCUDAPrelude+        genPrelude TargetCUDA = const genCUDAPrelude  -- | Due to simplifications after kernel extraction, some threshold -- parameters may contain KernelPaths that reference threshold@@ -76,48 +73,44 @@ -- In-kernel name and per-workgroup size in bytes. type LocalMemoryUse = (VName, Count Bytes Exp) -newtype KernelRequirements =-  KernelRequirements { kernelLocalMemory :: [LocalMemoryUse] }--instance Semigroup KernelRequirements where-  KernelRequirements lm1 <> KernelRequirements lm2 =-    KernelRequirements (lm1<>lm2)--instance Monoid KernelRequirements where-  mempty = KernelRequirements mempty--newtype OpenClRequirements =-  OpenClRequirements { openclUsedTypes :: S.Set PrimType }+data KernelState =+  KernelState { kernelLocalMemory :: [LocalMemoryUse]+              , kernelFailures :: [FailureMsg]+              , kernelNextSync :: Int+              , kernelSyncPending :: Bool+                -- ^ Has a potential failure occurred sine the last+                -- ErrorSync?+              , kernelHasBarriers :: Bool+              } -instance Semigroup OpenClRequirements where-  OpenClRequirements ts1 <> OpenClRequirements ts2 =-    OpenClRequirements (ts1 <> ts2)+newKernelState :: [FailureMsg] -> KernelState+newKernelState failures = KernelState mempty failures 0 False False -instance Monoid OpenClRequirements where-  mempty = OpenClRequirements mempty+errorLabel :: KernelState -> String+errorLabel = ("error_"++) . show . kernelNextSync -data ToOpenCL = ToOpenCL { clExtraFuns :: M.Map Name ImpOpenCL.Function-                         , clKernels :: M.Map KernelName C.Func-                         , clRequirements :: OpenClRequirements+data ToOpenCL = ToOpenCL { clKernels :: M.Map KernelName (Safety, C.Func)+                         , clUsedTypes :: S.Set PrimType                          , clSizes :: M.Map Name SizeClass+                         , clFailures :: [FailureMsg]                          } -instance Semigroup ToOpenCL where-  ToOpenCL f1 k1 r1 sz1 <> ToOpenCL f2 k2 r2 sz2 =-    ToOpenCL (f1<>f2) (k1<>k2) (r1<>r2) (sz1<>sz2)+initialOpenCL :: ToOpenCL+initialOpenCL = ToOpenCL mempty mempty mempty mempty -instance Monoid ToOpenCL where-  mempty = ToOpenCL mempty mempty mempty mempty+type OnKernelM = ReaderT Name (StateT ToOpenCL (Either InternalError)) -type OnKernelM = ReaderT Name (WriterT ToOpenCL (Either InternalError))+addSize :: Name -> SizeClass -> OnKernelM ()+addSize key sclass =+  modify $ \s -> s { clSizes = M.insert key sclass $ clSizes s }  onHostOp :: KernelTarget -> HostOp -> OnKernelM OpenCL onHostOp target (CallKernel k) = onKernel target k onHostOp _ (ImpKernels.GetSize v key size_class) = do-  tell mempty { clSizes = M.singleton key size_class }+  addSize key size_class   return $ ImpOpenCL.GetSize v key onHostOp _ (ImpKernels.CmpSizeLe v key size_class x) = do-  tell mempty { clSizes = M.singleton key size_class }+  addSize key size_class   return $ ImpOpenCL.CmpSizeLe v key x onHostOp _ (ImpKernels.GetSizeMax v size_class) =   return $ ImpOpenCL.GetSizeMax v size_class@@ -125,17 +118,20 @@ onKernel :: KernelTarget -> Kernel -> OnKernelM OpenCL  onKernel target kernel = do-  let (kernel_body, requirements) =-        GenericC.runCompilerM mempty inKernelOperations blankNameSource mempty $+  failures <- gets clFailures+  let (kernel_body, cstate) =+        GenericC.runCompilerM mempty (inKernelOperations (kernelBody kernel))+        blankNameSource+        (newKernelState failures) $         GenericC.blockScope $ GenericC.compileCode $ kernelBody kernel+      kstate = GenericC.compUserState cstate        use_params = mapMaybe useAsParam $ kernelUses kernel        (local_memory_args, local_memory_params, local_memory_init) =         unzip3 $         flip evalState (blankNameSource :: VNameSource) $-        mapM (prepareLocalMemory target) $ kernelLocalMemory $-        GenericC.compUserState requirements+        mapM (prepareLocalMemory target) $ kernelLocalMemory kstate        -- CUDA has very strict restrictions on the number of blocks       -- permitted along the 'y' and 'z' dimensions of the grid@@ -158,22 +154,70 @@                  [C.citem|const int block_dim1 = 1;|],                  [C.citem|const int block_dim2 = 2;|]]) -      params = perm_params ++ catMaybes local_memory_params ++ use_params+      (const_defs, const_undefs) = unzip $ mapMaybe constDef $ kernelUses kernel -      const_defs = mapMaybe constDef $ kernelUses kernel+  let (safety, error_init)+        | length (kernelFailures kstate) == length failures =+            if kernelFailureTolerant kernel+            then (SafetyNone, [])+            else -- No possible failures in this kernel, so if we make+                 -- it past an initial check, then we are good to go.+                 (SafetyCheap,+                  [C.citems|if (*global_failure >= 0) { return; }|]) -  tell mempty { clExtraFuns = mempty-              , clKernels = M.singleton name-                            [C.cfun|__kernel void $id:name ($params:params) {-                                $items:const_defs-                                $items:block_dim_init-                                $items:local_memory_init-                                $items:kernel_body-                                }|]-              , clRequirements = OpenClRequirements (typesInKernel kernel)-              }+        | otherwise =+            if not (kernelHasBarriers kstate)+            then (SafetyFull,+                  [C.citems|if (*global_failure >= 0) { return; }|])+            else (SafetyFull,+                  [C.citems|+                     volatile __local bool local_failure;+                     if (failure_is_an_option) {+                       if (get_local_id(0) == 0) {+                         local_failure = *global_failure >= 0;+                       }+                       barrier(CLK_LOCAL_MEM_FENCE);+                       if (local_failure) { return; }+                     } else {+                       local_failure = false;+                     }+                     barrier(CLK_LOCAL_MEM_FENCE);+                  |]) -  return $ LaunchKernel name (catMaybes local_memory_args ++ kernelArgs kernel) num_groups group_size+      failure_params =+        [[C.cparam|__global int *global_failure|],+         [C.cparam|int failure_is_an_option|],+         [C.cparam|__global int *global_failure_args|]]++      params = perm_params +++               take (numFailureParams safety) failure_params +++               catMaybes local_memory_params +++               use_params++      kernel_fun =+        [C.cfun|__kernel void $id:name ($params:params) {+                  $items:const_defs+                  $items:block_dim_init+                  $items:local_memory_init+                  $items:error_init+                  $items:kernel_body++                  $id:(errorLabel kstate): return;++                  $items:const_undefs+                }|]+  modify $ \s -> s+    { clKernels = M.insert name (safety, kernel_fun) $ clKernels s+    , clUsedTypes = typesInKernel kernel <> clUsedTypes s+    , clFailures = kernelFailures kstate+    }++  -- The argument corresponding to the global_failure parameters is+  -- added automatically later.+  let args = catMaybes local_memory_args +++             kernelArgs kernel++  return $ LaunchKernel safety name args num_groups group_size   where name = nameToString $ kernelName kernel         num_groups = kernelNumGroups kernel         group_size = kernelGroupSize kernel@@ -201,10 +245,15 @@ useAsParam ConstUse{} =   Nothing -constDef :: KernelUse -> Maybe C.BlockItem-constDef (ConstUse v e) = Just [C.citem|const $ty:t $id:v = $exp:e';|]-  where t = GenericC.primTypeToCType $ primExpType e-        e' = compilePrimExp e+-- Constants are #defined as macros.  Since a constant name in one+-- kernel might potentially (although unlikely) also be used for+-- something else in another kernel, we #undef them after the kernel.+constDef :: KernelUse -> Maybe (C.BlockItem, C.BlockItem)+constDef (ConstUse v e) = Just ([C.citem|$escstm:def|],+                                [C.citem|$escstm:undef|])+  where e' = compilePrimExp e+        def = "#define " ++ pretty (C.toIdent v mempty) ++ " (" ++ pretty e' ++ ")"+        undef = "#undef " ++ pretty (C.toIdent v mempty) constDef _ = Nothing  openClCode :: [C.Func] -> String@@ -214,8 +263,8 @@           [[C.cedecl|$func:kernel_func|] |            kernel_func <- kernels ] -genOpenClPrelude :: OpenClRequirements -> [C.Definition]-genOpenClPrelude (OpenClRequirements ts) =+genOpenClPrelude :: S.Set PrimType -> [C.Definition]+genOpenClPrelude ts =   -- Clang-based OpenCL implementations need this for 'static' to work.   [ [C.cedecl|$esc:("#ifdef cl_clang_storage_class_specifiers")|]   , [C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable")|]@@ -286,8 +335,8 @@                   return atomicCAS(($ty:t *)p, cmp, val);                 }|] | t <- types] -genCUDAPrelude :: OpenClRequirements -> [C.Definition]-genCUDAPrelude (OpenClRequirements _) =+genCUDAPrelude :: [C.Definition]+genCUDAPrelude =   cudafy ++ cudaAtomicOps ++ ops   where ops = cIntOps ++ cFloat32Ops ++ cFloat32Funs ++ cFloat64Ops                 ++ cFloat64Funs ++ cFloatConvOps@@ -403,21 +452,42 @@         useToArg (ScalarUse v bt) = Just $ ValueKArg (LeafExp (ScalarVar v) bt) bt         useToArg ConstUse{}       = Nothing ---- Generating C+nextErrorLabel :: GenericC.CompilerM KernelOp KernelState String+nextErrorLabel =+  errorLabel <$> GenericC.getUserState -inKernelOperations :: GenericC.Operations KernelOp KernelRequirements-inKernelOperations = GenericC.Operations-                     { GenericC.opsCompiler = kernelOps-                     , GenericC.opsMemoryType = kernelMemoryType-                     , GenericC.opsWriteScalar = kernelWriteScalar-                     , GenericC.opsReadScalar = kernelReadScalar-                     , GenericC.opsAllocate = cannotAllocate-                     , GenericC.opsDeallocate = cannotDeallocate-                     , GenericC.opsCopy = copyInKernel-                     , GenericC.opsStaticArray = noStaticArrays-                     , GenericC.opsFatMemory = False-                     }-  where kernelOps :: GenericC.OpCompiler KernelOp KernelRequirements+incErrorLabel :: GenericC.CompilerM KernelOp KernelState ()+incErrorLabel =+  GenericC.modifyUserState $ \s -> s { kernelNextSync = kernelNextSync s + 1 }++pendingError :: Bool -> GenericC.CompilerM KernelOp KernelState ()+pendingError b =+  GenericC.modifyUserState $ \s -> s { kernelSyncPending = b }++hasCommunication :: ImpKernels.KernelCode -> Bool+hasCommunication = any communicates+  where communicates ErrorSync = True+        communicates LocalBarrier = True+        communicates GlobalBarrier = True+        communicates _ = False++inKernelOperations :: ImpKernels.KernelCode -> GenericC.Operations KernelOp KernelState+inKernelOperations body =+  GenericC.Operations+  { GenericC.opsCompiler = kernelOps+  , GenericC.opsMemoryType = kernelMemoryType+  , GenericC.opsWriteScalar = kernelWriteScalar+  , GenericC.opsReadScalar = kernelReadScalar+  , GenericC.opsAllocate = cannotAllocate+  , GenericC.opsDeallocate = cannotDeallocate+  , GenericC.opsCopy = copyInKernel+  , GenericC.opsStaticArray = noStaticArrays+  , GenericC.opsFatMemory = False+  , GenericC.opsError = errorInKernel+  }+  where has_communication = hasCommunication body++        kernelOps :: GenericC.OpCompiler KernelOp KernelState         kernelOps (GetGroupId v i) =           GenericC.stm [C.cstm|$id:v = get_group_id($int:i);|]         kernelOps (GetLocalId v i) =@@ -430,10 +500,12 @@           GenericC.stm [C.cstm|$id:v = get_global_size($int:i);|]         kernelOps (GetLockstepWidth v) =           GenericC.stm [C.cstm|$id:v = LOCKSTEP_WIDTH;|]-        kernelOps LocalBarrier =+        kernelOps LocalBarrier = do           GenericC.stm [C.cstm|barrier(CLK_LOCAL_MEM_FENCE);|]-        kernelOps GlobalBarrier =+          GenericC.modifyUserState $ \s -> s { kernelHasBarriers = True }+        kernelOps GlobalBarrier = do           GenericC.stm [C.cstm|barrier(CLK_GLOBAL_MEM_FENCE);|]+          GenericC.modifyUserState $ \s -> s { kernelHasBarriers = True }         kernelOps MemFenceLocal =           GenericC.stm [C.cstm|mem_fence_local();|]         kernelOps MemFenceGlobal =@@ -445,14 +517,25 @@           GenericC.stm [C.cstm|$id:name = $id:name';|]         kernelOps (LocalAlloc name size) = do           name' <- newVName $ pretty name ++ "_backing"-          GenericC.modifyUserState (<>KernelRequirements [(name', size)])+          GenericC.modifyUserState $ \s ->+            s { kernelLocalMemory = (name', size) : kernelLocalMemory s }           GenericC.stm [C.cstm|$id:name = (__local char*) $id:name';|]+        kernelOps ErrorSync = do+          label <- nextErrorLabel+          pending <- kernelSyncPending <$> GenericC.getUserState+          when pending $ do+            pendingError False+            GenericC.stm [C.cstm|$id:label: barrier(CLK_LOCAL_MEM_FENCE);|]+            GenericC.stm [C.cstm|if (local_failure) { return; }|]+          GenericC.stm [C.cstm|barrier(CLK_LOCAL_MEM_FENCE);|]+          GenericC.modifyUserState $ \s -> s { kernelHasBarriers = True }+          incErrorLabel         kernelOps (Atomic space aop) = atomicOps space aop          atomicCast s t = do           let volatile = [C.ctyquals|volatile|]-          quals <- case s of DefaultSpace -> pointerQuals "global"-                             Space sid    -> pointerQuals sid+          quals <- case s of Space sid    -> pointerQuals sid+                             _            -> pointerQuals "global"           return [C.cty|$tyquals:(volatile++quals) $ty:t|]          doAtomic s old arr ind val op ty = do@@ -498,45 +581,53 @@           cast <- atomicCast s [C.cty|int|]           GenericC.stm [C.cstm|$id:old = atomic_xchg(&(($ty:cast *)$id:arr)[$exp:ind'], $exp:val');|] -        cannotAllocate :: GenericC.Allocate KernelOp KernelRequirements+        cannotAllocate :: GenericC.Allocate KernelOp KernelState         cannotAllocate _ =           error "Cannot allocate memory in kernel" -        cannotDeallocate :: GenericC.Deallocate KernelOp KernelRequirements+        cannotDeallocate :: GenericC.Deallocate KernelOp KernelState         cannotDeallocate _ _ =           error "Cannot deallocate memory in kernel" -        copyInKernel :: GenericC.Copy KernelOp KernelRequirements+        copyInKernel :: GenericC.Copy KernelOp KernelState         copyInKernel _ _ _ _ _ _ _ =           error "Cannot bulk copy in kernel." -        noStaticArrays :: GenericC.StaticArray KernelOp KernelRequirements+        noStaticArrays :: GenericC.StaticArray KernelOp KernelState         noStaticArrays _ _ _ _ =           error "Cannot create static array in kernel." -        kernelMemoryType space-          | Just t <- M.lookup space allScalarMemory =-              return $ GenericC.primTypeToCType t-         kernelMemoryType space = do           quals <- pointerQuals space           return [C.cty|$tyquals:quals $ty:defaultMemBlockType|] -        kernelWriteScalar dest _ _ space _ v-          | space `M.member` allScalarMemory =-              GenericC.stm [C.cstm|$exp:dest = $exp:v;|]--        kernelWriteScalar dest i elemtype space vol v =+        kernelWriteScalar =           GenericC.writeScalarPointerWithQuals pointerQuals-          dest i elemtype space vol v -        kernelReadScalar dest _ _ space _-          | space `M.member` allScalarMemory =-              return dest--        kernelReadScalar dest i elemtype space vol =+        kernelReadScalar =           GenericC.readScalarPointerWithQuals pointerQuals-          dest i elemtype space vol++        errorInKernel msg@(ErrorMsg parts) backtrace = do+          n <- length . kernelFailures <$> GenericC.getUserState+          GenericC.modifyUserState $ \s ->+            s { kernelFailures = kernelFailures s ++ [FailureMsg msg backtrace] }+          let setArgs _ [] = return []+              setArgs i (ErrorString{} : parts') = setArgs i parts'+              setArgs i (ErrorInt32 x : parts') = do+                x' <- GenericC.compileExp x+                stms <- setArgs (i+1) parts'+                return $ [C.cstm|global_failure_args[$int:i] = $exp:x';|] : stms+          argstms <- setArgs (0::Int) parts+          label <- nextErrorLabel+          pendingError True+          let what_next+                | has_communication = [C.citems|local_failure = true;+                                                goto $id:label;|]+                | otherwise         = [C.citems|return;|]+          GenericC.stm [C.cstm|{ if (atomic_cmpxchg(global_failure, -1, $int:n) == -1)+                                 { $stms:argstms; }+                                 $items:what_next+                               }|]  --- Checking requirements 
src/Futhark/CodeGen/ImpGen/Kernels/Transpose.hs view
@@ -221,6 +221,7 @@   , kernelNumGroups = num_groups   , kernelGroupSize = group_size   , kernelName = nameFromString name+  , kernelFailureTolerant = True   }   where pad2DBytes k = k * (k + 1) * primByteSize t         block_size =
src/Futhark/Compiler.hs view
@@ -32,6 +32,7 @@ import qualified Futhark.TypeCheck as I import Futhark.Compiler.Program import Futhark.Util.Log+import Futhark.Util.Pretty (prettyText)  data FutharkConfig = FutharkConfig                      { futharkVerbose :: (Verbosity, Maybe FilePath)@@ -51,8 +52,9 @@ dumpError config err =   case err of     ExternalError s -> do-      T.hPutStrLn stderr s-      T.hPutStrLn stderr "If you find this error message confusing, uninformative, or wrong, please open an issue at https://github.com/diku-dk/futhark/issues."+      T.hPutStrLn stderr $ prettyText s+      T.hPutStrLn stderr ""+      T.hPutStrLn stderr "If you find this error message confusing, uninformative, or wrong, please open an issue at\nhttps://github.com/diku-dk/futhark/issues."     InternalError s info CompilerBug -> do       T.hPutStrLn stderr "Internal compiler error."       T.hPutStrLn stderr "Please report this at https://github.com/diku-dk/futhark/issues."
src/Futhark/Compiler/CLI.hs view
@@ -75,6 +75,9 @@   , Option [] ["executable"]     (NoArg $ Right $ \config -> config { compilerMode = ToExecutable })     "Generate an executable instead of a library (set by default)."+  , Option "w" []+    (NoArg $ Right $ \config -> config { compilerWarn = False })+    "Disable all warnings."   , Option [] ["Werror"]     (NoArg $ Right $ \config -> config { compilerWerror = True })     "Treat warnings as errors."@@ -103,6 +106,7 @@                  , compilerMode :: CompilerMode                  , compilerWerror :: Bool                  , compilerSafe :: Bool+                 , compilerWarn :: Bool                  , compilerConfig :: cfg                  } @@ -116,6 +120,7 @@                                      , compilerMode = ToExecutable                                      , compilerWerror = False                                      , compilerSafe = False+                                     , compilerWarn = True                                      , compilerConfig = x                                      } @@ -128,4 +133,5 @@   newFutharkConfig { futharkVerbose = compilerVerbose config                    , futharkWerror = compilerWerror config                    , futharkSafe = compilerSafe config+                   , futharkWarn = compilerWarn config                    }
src/Futhark/Compiler/Program.hs view
@@ -35,7 +35,8 @@ import qualified Language.Futhark as E import qualified Language.Futhark.TypeChecker as E import Language.Futhark.Semantic-import Language.Futhark.Futlib+import Language.Futhark.Prelude+import Futhark.Util.Pretty (ppr)  -- | A little monad for reading and type-checking a Futhark program. type CompilerM m = ReaderT [FilePath] (StateT ReaderState m)@@ -65,7 +66,7 @@               [ImportName] -> ImportName -> CompilerM m () readImport steps include   | include `elem` steps =-      throwError $ ExternalError $ T.pack $+      externalErrorS $       "Import cycle: " ++ intercalate " -> "       (map includeToString $ reverse $ include:steps)   | otherwise = do@@ -96,7 +97,7 @@    case E.checkProg imports src include $ prependRoots roots prog of     Left err ->-      externalError $ T.pack $ show err+      externalError $ ppr err     Right (m, ws, src') ->       modify $ \s ->         s { alreadyImported = (includeToString include,m) : imports@@ -121,12 +122,12 @@   -- then we look at the builtin library if we have to.  For the   -- builtins, we don't use the search path.   r <- liftIO $ readFileSafely $ includeToFilePath include-  case (r, lookup futlib_str futlib) of+  case (r, lookup prelude_str prelude) of     (Just (Right (filepath,s)), _) -> return (s, filepath)     (Just (Left e), _)  -> externalErrorS e-    (Nothing, Just t)   -> return (t, futlib_str)+    (Nothing, Just t)   -> return (t, prelude_str)     (Nothing, Nothing)  -> externalErrorS not_found-   where futlib_str = "/" Posix.</> includeToString include Posix.<.> "fut"+   where prelude_str = "/" Posix.</> includeToString include Posix.<.> "fut"           not_found =            "Error at " ++ E.locStr (srclocOf include) ++@@ -141,8 +142,8 @@                            VNameSource) readLibraryWithBasis builtin fps = do   (_, imps, src) <- runCompilerM builtin $-    mapM (readImport [] . mkInitialImport) prelude-  let basis = Basis imps src prelude+    readImport [] $ mkInitialImport "/prelude/prelude"+  let basis = Basis imps src ["/prelude/prelude"]   readLibrary' basis fps  -- | Read and type-check a Futhark library (multiple files, relative@@ -158,12 +159,12 @@           case r of             Just (Right (_, fs)) ->               handleFile [] (mkInitialImport fp_name) fs fp-            Just (Left e) -> externalError $ T.pack e+            Just (Left e) -> externalErrorS e             Nothing -> externalErrorS $ fp ++ ": file not found."             where (fp_name, _) = Posix.splitExtension fp  -- | Read and type-check Futhark imports (no @.fut@ extension; may--- refer to baked-in futlib).  This is an exotic operation that+-- refer to baked-in prelude).  This is an exotic operation that -- probably only makes sense in an interactive environment. readImports :: (MonadError CompilerError m, MonadIO m) =>                Basis -> [ImportName]
src/Futhark/Construct.hs view
@@ -42,6 +42,7 @@   , foldBinOp   , binOpLambda   , cmpOpLambda+  , sliceDim   , fullSlice   , fullSliceNum   , isFullSlice@@ -88,7 +89,7 @@   idents <- letBindNames vs e   case idents of     [ident] -> return $ identName ident-    _       -> fail $ "letExp: tuple-typed expression given:\n" ++ pretty e+    _       -> error $ "letExp: tuple-typed expression given:\n" ++ pretty e  letInPlace :: MonadBinder m =>               String -> VName -> Slice SubExp -> Exp (Lore m)@@ -188,7 +189,7 @@       return $ BasicOp $       BinOp (FSub float_t) (floatConst float_t 0) e'     _ ->-      fail $ "eNegate: operand " ++ pretty e ++ " has invalid type."+      error $ "eNegate: operand " ++ pretty e ++ " has invalid type."  eNot :: MonadBinder m =>         m (Exp (Lore m)) -> m (Exp (Lore m))@@ -206,7 +207,7 @@     Prim (FloatType float_t) ->       return $ BasicOp $ UnOp (FAbs float_t) e'     _ ->-      fail $ "eAbs: operand " ++ pretty e ++ " has invalid type."+      error $ "eAbs: operand " ++ pretty e ++ " has invalid type."  eSignum :: MonadBinder m =>         m (Exp (Lore m)) -> m (Exp (Lore m))@@ -218,7 +219,7 @@     Prim (IntType int_t) ->       return $ BasicOp $ UnOp (SSignum int_t) e'     _ ->-      fail $ "eSignum: operand " ++ pretty e ++ " has invalid type."+      error $ "eSignum: operand " ++ pretty e ++ " has invalid type."  eCopy :: MonadBinder m =>          m (Exp (Lore m)) -> m (Exp (Lore m))@@ -313,7 +314,7 @@ eBlank :: MonadBinder m => Type -> m (Exp (Lore m)) eBlank (Prim t) = return $ BasicOp $ SubExp $ Constant $ blankPrimValue t eBlank (Array pt shape _) = return $ BasicOp $ Scratch pt $ shapeDims shape-eBlank Mem{} = fail "eBlank: cannot create blank memory"+eBlank Mem{} = error "eBlank: cannot create blank memory"  -- | Sign-extend to the given integer type. asIntS :: MonadBinder m => IntType -> SubExp -> m SubExp@@ -331,7 +332,7 @@     Prim (IntType from_it)       | to_it == from_it -> return e       | otherwise -> letSubExp s $ BasicOp $ ConvOp (ext from_it to_it) e-    _ -> fail "asInt: wrong type"+    _ -> error "asInt: wrong type"   where s = case e of Var v -> baseString v                       _     -> "to_" ++ pretty to_it @@ -373,6 +374,7 @@            , lambdaBody       = body            } +-- | Slice a full dimension of the given size. sliceDim :: SubExp -> DimIndex SubExp sliceDim d = DimSlice (constant (0::Int32)) d (constant (1::Int32)) 
src/Futhark/Doc/Generator.hs view
@@ -308,7 +308,7 @@   return $ specRow lhs (mhs <> " : ") rhs  valBindHtml :: Html -> ValBind -> DocM (Html, Html, Html)-valBindHtml name (ValBind _ _ retdecl (Info rettype) tparams params _ _ _) = do+valBindHtml name (ValBind _ _ retdecl (Info (rettype, _)) tparams params _ _ _) = do   let tparams' = mconcat $ map ((" "<>) . typeParamHtml) tparams       noLink' = noLink $ map typeParamName tparams ++                 map identName (S.toList $ mconcat $ map patternIdents params)@@ -701,8 +701,10 @@  valBindWhat :: ValBind -> IndexWhat valBindWhat vb | null (valBindParams vb),-                 orderZero (unInfo (valBindRetType vb)) = IndexValue-               | otherwise                              = IndexFunction+                 orderZero (fst $ unInfo $ valBindRetType vb) =+                   IndexValue+               | otherwise =+                   IndexFunction  describeSpecs :: [Spec] -> DocM Html describeSpecs specs =
src/Futhark/Error.hs view
@@ -18,6 +18,7 @@  import Control.Monad.Error.Class import qualified Data.Text as T+import Futhark.Util.Pretty  -- | There are two classes of internal errors: actual bugs, and -- implementation limitations.  The latter are already known and need@@ -27,7 +28,7 @@                 deriving (Eq, Ord, Show)  data CompilerError =-    ExternalError T.Text+    ExternalError Doc     -- ^ An error that happened due to something the user did, such as     -- provide incorrect code or options.   | InternalError T.Text T.Text ErrorClass@@ -35,14 +36,14 @@     -- for debugging, which can be written to a file.  instance Show CompilerError where-  show (ExternalError s) = T.unpack s+  show (ExternalError s) = pretty s   show (InternalError s _ _) = T.unpack s -externalError :: MonadError CompilerError m => T.Text -> m a+externalError :: MonadError CompilerError m => Doc -> m a externalError = throwError . ExternalError  externalErrorS :: MonadError CompilerError m => String -> m a-externalErrorS = externalError . T.pack+externalErrorS = externalError . text  -- | An error that is not the users fault, but a bug (or limitation) -- in the compiler.  Compiler passes should only ever report this
src/Futhark/FreshNames.hs view
@@ -31,7 +31,8 @@  -- | Produce a fresh name, using the given name as a template. newName :: VNameSource -> VName -> (VName, VNameSource)-newName (VNameSource i) k = (VName (baseName k) i, VNameSource (i+1))+newName (VNameSource i) k = i' `seq` (VName (baseName k) i, VNameSource i')+  where i' = i+1  -- | A blank name source. blankNameSource :: VNameSource
src/Futhark/Internalise.hs view
@@ -12,6 +12,7 @@  import Control.Monad.State import Control.Monad.Reader+import Data.Bifunctor (first) import Data.Bitraversable import qualified Data.Map.Strict as M import qualified Data.Set as S@@ -66,9 +67,9 @@     Nothing -> return $ nameFromString $ pretty ofname  internaliseValBind :: E.ValBind -> InternaliseM ()-internaliseValBind fb@(E.ValBind entry fname retdecl (Info rettype) tparams params body _ loc) = do+internaliseValBind fb@(E.ValBind entry fname retdecl (Info (rettype, retext)) tparams params body _ loc) = do   bindingParams tparams params $ \pcm shapeparams params' -> do-    (rettype_bad, rcm) <- internaliseReturnType rettype+    (rettype_bad, rcm) <- internaliseReturnType =<< existentialisedRetExt     let rettype' = zeroExts rettype_bad      let mkConstParam name = Param name $ I.Prim int32@@ -109,7 +110,9 @@     if null params'       then bindConstant fname (fname',                                pcm<>rcm,-                               applyRetType rettype' constparams)+                               applyRetType rettype' constparams,+                               bindExtSizes rettype retext,+                               namesFromList retext)       else bindFunction fname (fname',                                pcm<>rcm,                                map I.paramName free_params,@@ -127,6 +130,13 @@     -- them from somewhere else.     zeroExts ts = generaliseExtTypes ts ts +    existentialisedRetExt  = do+      sizes <- (namesFromList retext<>) <$> topLevelSizes+      let onDim (NamedDim v)+            | qualLeaf v `nameIn` sizes = AnyDim+          onDim d = d+      return $ first onDim rettype+ allDimsFreshInType :: MonadFreshNames m => E.PatternType -> m E.PatternType allDimsFreshInType = bitraverse onDim pure   where onDim (E.NamedDim v) =@@ -157,14 +167,14 @@   mapM allDimsFreshInPat pats <*> pure loc  generateEntryPoint :: E.StructType -> E.ValBind -> InternaliseM ()-generateEntryPoint ftype (E.ValBind _ ofname retdecl (Info rettype) _ params _ _ loc) = do+generateEntryPoint ftype (E.ValBind _ ofname retdecl (Info (rettype, _)) _ params _ _ loc) = do   -- We replace all shape annotations, so there should be no constant   -- parameters here.   params_fresh <- mapM allDimsFreshInPat params   let tparams = map (`E.TypeParamDim` noLoc) $ S.toList $                 mconcat $ map E.patternDimNames params_fresh   bindingParams tparams params_fresh $ \_ shapeparams params' -> do-    (entry_rettype, _) <- internaliseEntryReturnType $ anyDimShapeAnnotations rettype+    (entry_rettype, _) <- internaliseEntryReturnType $ anySizes rettype     let (e_paramts, e_rettype) = E.unfoldFunType ftype         entry' = entryPoint (zip3 params e_paramts params') (retdecl, e_rettype, entry_rettype)         args = map (I.Var . I.paramName) $ concat params'@@ -220,7 +230,7 @@         entryPointType (te, t, ts) =           [I.TypeOpaque desc $ length ts]           where desc = maybe (pretty t') typeExpOpaqueName te-                t' = removeShapeAnnotations t `E.setUniqueness` Nonunique+                t' = noSizes t `E.setUniqueness` Nonunique          -- | We remove dimension arguments such that we hopefully end         -- up with a simpler type name for the entry point.  The@@ -244,19 +254,18 @@ internaliseIdent (E.Ident name (Info tp) loc) =   case tp of     E.Scalar E.Prim{} -> return name-    _ -> fail $ "Futhark.Internalise.internaliseIdent: asked to internalise non-prim-typed ident '"+    _ -> error $ "Futhark.Internalise.internaliseIdent: asked to internalise non-prim-typed ident '"          ++ pretty name ++ " of type " ++ pretty tp ++          " at " ++ locStr loc ++ "."  internaliseBody :: E.Exp -> InternaliseM Body internaliseBody e = insertStmsM $ resultBody <$> internaliseExp "res" e -internaliseBodyStms :: E.Exp -> ([SubExp] -> InternaliseM (Body, a))-                    -> InternaliseM (Body, a)-internaliseBodyStms e m = do-  ((Body _ bnds res,x), otherbnds) <--    collectStms $ m =<< internaliseExp "res" e-  (,x) <$> mkBodyM (otherbnds <> bnds) res+bodyFromStms :: InternaliseM (Result, a)+             -> InternaliseM (Body, a)+bodyFromStms m = do+  ((res, a), stms) <- collectStms m+  (,a) <$> mkBodyM stms res  internaliseExp :: String -> E.Exp -> InternaliseM [I.SubExp] @@ -282,15 +291,24 @@         Just ses -> return ses         Nothing -> (:[]) . I.Var <$> internaliseIdent (E.Ident name (Info t) loc) -internaliseExp desc (E.Index e idxs _ loc) = do+internaliseExp desc (E.Index e idxs (Info ret, Info retext) loc) = do   vs <- internaliseExpToVars "indexed" e   dims <- case vs of []  -> return [] -- Will this happen?                      v:_ -> I.arrayDims <$> lookupType v   (idxs', cs) <- internaliseSlice loc dims idxs   let index v = do v_t <- lookupType v                    return $ I.BasicOp $ I.Index v $ fullSlice v_t idxs'-  certifying cs $ letSubExps desc =<< mapM index vs+  ses <- certifying cs $ letSubExps desc =<< mapM index vs+  bindExtSizes (E.toStruct ret) retext ses+  return ses +-- XXX: we map empty records and tuples to bools, because otherwise+-- arrays of unit will lose their sizes.+internaliseExp _ (E.TupLit [] _) =+  return [constant True]+internaliseExp _ (E.RecordLit [] _) =+  return [constant True]+ internaliseExp desc (E.TupLit es _) = concat <$> mapM (internaliseExp desc) es  internaliseExp desc (E.RecordLit orig_fields _) =@@ -321,25 +339,36 @@         letSubExp desc $ I.BasicOp $ I.Reshape new_shape' flat_arr    | otherwise = do-  es' <- mapM (internaliseExp "arr_elem") es-  case es' of-    [] -> do-      rowtypes <- internaliseType $ toStructural rowtype-      let arraylit rt = I.BasicOp $ I.ArrayLit [] rt-      letSubExps desc $ map (arraylit . zeroDim . fromDecl) rowtypes-    e' : _ -> do-      rowtypes <- mapM subExpType e'+      (arr_t_ext, cm) <- internaliseReturnType $ E.toStruct arr_t+      mapM_ (uncurry (internaliseDimConstant loc)) cm+      es' <- mapM (internaliseExp "arr_elem") es++      let typeFromElements =+            -- XXX: the monomorphiser may create single-element array+            -- literals with an unknown row type.  In those cases we+            -- need to look at the types of the actual elements.+            -- Fixing this in the monomorphiser is a lot more tricky+            -- than just working around it here.+            case es' of+              [] -> error $ "internaliseExp ArrayLit: existential type: " ++ pretty arr_t+              e':_ -> mapM subExpType e'++      rowtypes <-+        maybe typeFromElements pure $+        mapM (fmap rowType . hasStaticShape . I.fromDecl) arr_t_ext+       let arraylit ks rt = do-            ks' <- mapM (ensureShape asserting "shape of element differs from shape of first element"+            ks' <- mapM (ensureShape asserting+                         "shape of element differs from shape of first element"                          loc rt "elem_reshaped") ks             return $ I.BasicOp $ I.ArrayLit ks' rt-      letSubExps desc =<< zipWithM arraylit (transpose es') rowtypes-  where rowtype = E.stripArray 1 arr_t -        zeroDim t = t `I.setArrayShape`-                    I.Shape (replicate (I.arrayRank t) (constant (0::Int32)))+      letSubExps desc =<<+        if null es'+        then mapM (arraylit []) rowtypes+        else zipWithM arraylit (transpose es') rowtypes -        isArrayLiteral :: E.Exp -> Maybe ([Int],[E.Exp])+  where isArrayLiteral :: E.Exp -> Maybe ([Int],[E.Exp])         isArrayLiteral (E.ArrayLit inner_es _ _) = do           (eshape,e):inner_es' <- mapM isArrayLiteral inner_es           guard $ all ((eshape==) . fst) inner_es'@@ -347,7 +376,7 @@         isArrayLiteral e =           Just ([], [e]) -internaliseExp desc (E.Range start maybe_second end _ loc) = do+internaliseExp desc (E.Range start maybe_second end (Info ret, Info retext) loc) = do   start' <- internaliseExp1 "range_start" start   end' <- internaliseExp1 "range_end" $ case end of     DownToExclusive e -> e@@ -380,7 +409,7 @@     case E.typeOf start of       E.Scalar (E.Prim (E.Signed it)) -> return (it, CmpSle it, CmpSlt it)       E.Scalar (E.Prim (E.Unsigned it)) -> return (it, CmpUle it, CmpUlt it)-      start_t -> fail $ "Start value in range has type " ++ pretty start_t+      start_t -> error $ "Start value in range has type " ++ pretty start_t    let one = intConst it 1       negone = intConst it (-1)@@ -458,14 +487,19 @@   step_i32 <- asIntS Int32 step   pos_step <- letSubExp "pos_step" $               I.BasicOp $ I.BinOp (Mul Int32) step_i32 step_sign_i32+   num_elems <- certifying cs $                letSubExp "num_elems" =<<                eDivRoundingUp Int32 (eSubExp distance) (eSubExp pos_step)    se <- letSubExp desc (I.BasicOp $ I.Iota num_elems start' step it)+  bindExtSizes (E.toStruct ret) retext [se]   return [se] -internaliseExp desc (E.Ascript e (TypeDecl dt (Info et)) _ loc) = do+internaliseExp desc (E.Ascript e _ _) =+  internaliseExp desc e++internaliseExp desc (E.Coerce e (TypeDecl dt (Info et)) _ loc) = do   es <- internaliseExp desc e   (ts, cm) <- internaliseReturnType et   mapM_ (uncurry (internaliseDimConstant loc)) cm@@ -484,96 +518,102 @@                letTupExp' desc $ I.BasicOp $ I.BinOp (I.Sub t) (I.intConst t 0) e'              I.Prim (I.FloatType t) ->                letTupExp' desc $ I.BasicOp $ I.BinOp (I.FSub t) (I.floatConst t 0) e'-             _ -> fail "Futhark.Internalise.internaliseExp: non-numeric type in Negate"+             _ -> error "Futhark.Internalise.internaliseExp: non-numeric type in Negate"  internaliseExp desc e@E.Apply{} = do-  (qfname, args) <- findFuncall e+  (qfname, args, ret, retext) <- findFuncall e+  -- Argument evaluation is outermost-in so that any existential sizes+  -- created by function applications can be brought into scope.   let fname = nameFromString $ pretty $ baseName $ qualLeaf qfname       loc = srclocOf e       arg_desc = nameToString fname ++ "_arg"    -- Some functions are magical (overloaded) and we handle that here.-  -- Note that polymorphic functions (which are not magical) are not-  -- handled here.-  case () of-    () | Just internalise <- isOverloadedFunction qfname args loc ->-           internalise desc-       | Just (rettype, _) <- M.lookup fname I.builtInFunctions -> do-           let tag ses = [ (se, I.Observe) | se <- ses ]-           args' <- mapM (internaliseExp arg_desc) args-           let args'' = concatMap tag args'-           letTupExp' desc $ I.Apply fname args'' [I.Prim rettype] (Safe, loc, [])-       | otherwise -> do-           args' <- concat <$> mapM (internaliseExp arg_desc) args-           fst <$> funcall desc qfname args' loc+  ses <-+    case () of+      -- Overloaded functions never take array arguments (except+      -- equality, but those cannot be existential), so we can safely+      -- ignore the existential dimensions.+      () | Just internalise <- isOverloadedFunction qfname (map fst args) loc ->+             internalise desc -internaliseExp desc (E.LetPat pat e body _ loc) =-  internalisePat desc pat e body loc (internaliseExp desc)+         | Just (rettype, _) <- M.lookup fname I.builtInFunctions -> do+             let tag ses = [ (se, I.Observe) | se <- ses ]+             args' <- reverse <$> mapM (internaliseArg arg_desc) (reverse args)+             let args'' = concatMap tag args'+             letTupExp' desc $ I.Apply fname args'' [I.Prim rettype] (Safe, loc, []) -internaliseExp desc (E.LetFun ofname (tparams, params, retdecl, Info rettype, body) letbody loc) = do-  internaliseValBind $ E.ValBind Nothing ofname retdecl (Info rettype) tparams params body Nothing loc-  internaliseExp desc letbody+         | otherwise -> do+             args' <- concat . reverse <$> mapM (internaliseArg arg_desc) (reverse args)+             fst <$> funcall desc qfname args' loc -internaliseExp desc (E.DoLoop mergepat mergeexp form loopbody loc) = do-  -- We pretend that we saw a let-binding first to ensure that the-  -- initial values for the merge parameters match their annotated-  -- sizes-  ses <- internaliseExp "loop_init" mergeexp-  t <- I.staticShapes <$> mapM I.subExpType ses-  stmPattern mergepat t $ \cm mergepat_names match -> do-    mapM_ (uncurry (internaliseDimConstant loc)) cm-    ses' <- match (srclocOf mergepat) ses-    forM_ (zip mergepat_names ses') $ \(v,se) ->-      letBindNames_ [v] $ I.BasicOp $ I.SubExp se-    let mergeinit = map I.Var mergepat_names+  bindExtSizes ret retext ses+  return ses -    (loopbody', (form', shapepat, mergepat', mergeinit', pre_stms)) <--      handleForm mergeinit form+internaliseExp desc (E.LetPat pat e body (Info ret, Info retext) loc) = do+  ses <- internalisePat desc pat e body loc (internaliseExp desc)+  bindExtSizes (E.toStruct ret) retext ses+  return ses -    addStms pre_stms+internaliseExp desc (E.LetFun ofname (tparams, params, retdecl, Info rettype, body) letbody _ loc) = do+  internaliseValBind $ E.ValBind Nothing ofname retdecl (Info (rettype, [])) tparams params body Nothing loc+  internaliseExp desc letbody -    mergeinit_ts' <- mapM subExpType mergeinit'+internaliseExp desc (E.DoLoop sparams mergepat mergeexp form loopbody (Info (ret, retext)) loc) = do+  ses <- internaliseExp "loop_init" mergeexp+  ((loopbody', (form', shapepat, mergepat', mergeinit')), initstms) <-+    collectStms $ handleForm ses form -    ctxinit <- argShapes-               (map I.paramName shapepat)-               (map I.paramType mergepat')-               mergeinit_ts'-    let ctxmerge = zip shapepat ctxinit-        valmerge = zip mergepat' mergeinit'-        merge = ctxmerge ++ valmerge-        dropCond = case form of E.While{} -> drop 1-                                _         -> id+  addStms initstms+  mergeinit_ts' <- mapM subExpType mergeinit' -    -- Ensure that the result of the loop matches the shapes of the-    -- merge parameters, if any have been annotated by programmer.-    let merge_names = map (I.paramName . fst) merge-        merge_ts = existentialiseExtTypes merge_names $-                   staticShapes $ map (I.paramType . fst) merge-    loopbody'' <- localScope (scopeOfFParams $ map fst merge) $-                  inScopeOf form' $-                  ensureResultExtShapeNoCtx asserting-                  "shape of loop result does not match shapes in loop parameters"-                  loc merge_ts loopbody'+  let ctxinit = argShapes+                (map I.paramName shapepat)+                (map I.paramType mergepat')+                mergeinit_ts'+      ctxmerge = zip shapepat ctxinit+      valmerge = zip mergepat' mergeinit'+      dropCond = case form of E.While{} -> drop 1+                              _         -> id -    loop_res <- letTupExp desc $ I.DoLoop ctxmerge valmerge form' loopbody''-    return $ map I.Var $ dropCond loop_res+  -- Ensure that the result of the loop matches the shapes of the+  -- merge parameters.  XXX: Ideally they should already match (by+  -- the source language type rules), but some of our+  -- transformations (esp. defunctionalisation) strips out some size+  -- information.  For a type-correct source program, these reshapes+  -- should simplify away.+  let merge = ctxmerge ++ valmerge+      merge_names = map (I.paramName . fst) merge+      merge_ts = existentialiseExtTypes merge_names $+                 staticShapes $ map (I.paramType . fst) merge+  loopbody'' <- localScope (scopeOfFParams $ map fst merge) $+                inScopeOf form' $+                ensureResultExtShapeNoCtx asserting+                "shape of loop result does not match shapes in loop parameter"+                loc merge_ts loopbody' +  loop_res <- map I.Var . dropCond <$>+              letTupExp desc (I.DoLoop ctxmerge valmerge form' loopbody'')+  bindExtSizes (E.toStruct ret) retext loop_res+  return loop_res+   where-    forLoop nested_mergepat shapepat mergeinit form' =-      inScopeOf form' $ internaliseBodyStms loopbody $ \ses -> do-      sets <- mapM subExpType ses-      let mergepat' = concat nested_mergepat-      shapeargs <- argShapes-                   (map I.paramName shapepat)-                   (map I.paramType mergepat')-                   sets-      return (resultBody $ shapeargs ++ ses,-              (form',-               shapepat,-               mergepat',-               mergeinit,-               mempty))+    sparams' = map (`TypeParamDim` noLoc) sparams +    forLoop nested_mergepat shapepat mergeinit form' = do+      let mergepat' = concat nested_mergepat+      bodyFromStms $ inScopeOf form' $ do+        ses <- internaliseExp "loopres" loopbody+        sets <- mapM subExpType ses+        let shapeargs = argShapes+                        (map I.paramName shapepat)+                        (map I.paramType mergepat')+                        sets+        return (shapeargs ++ ses,+                (form',+                 shapepat,+                 mergepat',+                 mergeinit))      handleForm mergeinit (E.ForIn x arr) = do       arr' <- internaliseExpToVars "for_in_arr" arr@@ -582,7 +622,8 @@        i <- newVName "i" -      bindingParams [] [mergepat] $ \mergecm shapepat nested_mergepat ->+      bindingParams sparams' [mergepat] $+        \mergecm shapepat nested_mergepat ->         bindingLambdaParams [x] (map rowType arr_ts) $ \x_cm x_params -> do           mapM_ (uncurry (internaliseDimConstant loc)) x_cm           mapM_ (uncurry (internaliseDimConstant loc)) mergecm@@ -595,27 +636,28 @@       num_iterations_t <- I.subExpType num_iterations'       it <- case num_iterations_t of               I.Prim (IntType it) -> return it-              _                   -> fail "internaliseExp DoLoop: invalid type"+              _                   -> error "internaliseExp DoLoop: invalid type" -      bindingParams [] [mergepat] $ \mergecm shapepat nested_mergepat -> do-        mapM_ (uncurry (internaliseDimConstant loc)) mergecm-        forLoop nested_mergepat shapepat mergeinit $ I.ForLoop i' it num_iterations' []+      bindingParams sparams' [mergepat] $+        \mergecm shapepat nested_mergepat -> do+          mapM_ (uncurry (internaliseDimConstant loc)) mergecm+          forLoop nested_mergepat shapepat mergeinit $ I.ForLoop i' it num_iterations' []      handleForm mergeinit (E.While cond) =-      bindingParams [] [mergepat] $ \mergecm shapepat nested_mergepat -> do+      bindingParams sparams' [mergepat] $ \mergecm shapepat nested_mergepat -> do         mergeinit_ts <- mapM subExpType mergeinit         mapM_ (uncurry (internaliseDimConstant loc)) mergecm         let mergepat' = concat nested_mergepat         -- We need to insert 'cond' twice - once for the initial-        -- condition (do we enter the loop at all?), and once with-        -- the result values of the loop (do we continue into the-        -- next iteration?).  This is safe, as the type rules for-        -- the external language guarantees that 'cond' does not-        -- consume anything.-        shapeinit <- argShapes-                     (map I.paramName shapepat)-                     (map I.paramType mergepat')-                     mergeinit_ts+        -- condition (do we enter the loop at all?), and once with the+        -- result values of the loop (do we continue into the next+        -- iteration?).  This is safe, as the type rules for the+        -- external language guarantees that 'cond' does not consume+        -- anything.+        let shapeinit = argShapes+                        (map I.paramName shapepat)+                        (map I.paramType mergepat')+                        mergeinit_ts          (loop_initial_cond, init_loop_cond_bnds) <- collectStms $ do           forM_ (zip shapepat shapeinit) $ \(p, se) ->@@ -628,13 +670,16 @@                        _ -> SubExp se           internaliseExp1 "loop_cond" cond -        internaliseBodyStms loopbody $ \ses -> do+        addStms init_loop_cond_bnds++        bodyFromStms $ do+          ses <- internaliseExp "loopres" loopbody           sets <- mapM subExpType ses           loop_while <- newParam "loop_while" $ I.Prim I.Bool-          shapeargs <- argShapes-                       (map I.paramName shapepat)-                       (map I.paramType mergepat')-                       sets+          let shapeargs = argShapes+                          (map I.paramName shapepat)+                          (map I.paramType mergepat')+                          sets            -- Careful not to clobber anything.           loop_end_cond_body <- renameBody <=< insertStmsM $ do@@ -650,18 +695,17 @@             resultBody <$> internaliseExp "loop_cond" cond           loop_end_cond <- bodyBind loop_end_cond_body -          return (resultBody $ shapeargs++loop_end_cond++ses,+          return (shapeargs++loop_end_cond++ses,                   (I.WhileLoop $ I.paramName loop_while,                    shapepat,                    loop_while : mergepat',-                   loop_initial_cond : mergeinit,-                   init_loop_cond_bnds))+                   loop_initial_cond : mergeinit))  internaliseExp desc (E.LetWith name src idxs ve body t loc) = do   let pat = E.Id (E.identName name) (E.identType name) loc       src_t = E.fromStruct <$> E.identType src       e = E.Update (E.Var (E.qualName $ E.identName src) src_t loc) idxs ve loc-  internaliseExp desc $ E.LetPat pat e body t loc+  internaliseExp desc $ E.LetPat pat e body (t, Info []) loc  internaliseExp desc (E.Update src slice ve loc) = do   ves <- internaliseExp "lw_val" ve@@ -695,8 +739,9 @@         replace _ _ ve' _ = return ve'  internaliseExp desc (E.Unsafe e _) =-  local (\env -> env { envDoBoundsChecks = False }) $-  internaliseExp desc e+  local mkUnsafe $ internaliseExp desc e+  where mkUnsafe env | envSafe env = env+                     | otherwise = env { envDoBoundsChecks = False }  internaliseExp desc (E.Assert e1 e2 (Info check) loc) = do   e1' <- internaliseExp1 "assert_cond" e1@@ -721,7 +766,7 @@     Just (i, js) ->       (intConst Int8 (toInteger i):) <$> clauses 0 ts' (zip js es')     Nothing ->-      fail "internaliseExp Constr: missing constructor"+      error "internaliseExp Constr: missing constructor"    where clauses j (t:ts) js_to_es           | Just e <- j `lookup` js_to_es =@@ -733,11 +778,12 @@           return []  internaliseExp _ (E.Constr _ _ (Info t) loc) =-  fail $ "internaliseExp: constructor with type " ++ pretty t ++ " at " ++ locStr loc+  error $ "internaliseExp: constructor with type " ++ pretty t ++ " at " ++ locStr loc -internaliseExp desc (E.Match e cs _ _) = do+internaliseExp desc (E.Match e cs (Info ret, Info retext) _) = do   ses <- internaliseExp (desc ++ "_scrutinee") e-  case NE.uncons cs of+  res <-+    case NE.uncons cs of     (CasePat pCase eCase locCase, Nothing) -> do       (_, pertinent) <- generateCond pCase ses       internalisePat' pCase pertinent eCase locCase (internaliseExp desc)@@ -749,6 +795,8 @@         foldM (\bf c' -> eBody $ return $ generateCaseIf ses c' bf) eLast' $           reverse $ NE.init cs'       letTupExp' desc =<< generateCaseIf ses c bFalse+  bindExtSizes (E.toStruct ret) retext res+  return res  -- The "interesting" cases are over, now it's mostly boilerplate. @@ -763,30 +811,35 @@       return [I.Constant $ I.IntValue $ intValue it v]     E.Scalar (E.Prim (E.FloatType ft)) ->       return [I.Constant $ I.FloatValue $ floatValue ft v]-    _ -> fail $ "internaliseExp: nonsensical type for integer literal: " ++ pretty t+    _ -> error $ "internaliseExp: nonsensical type for integer literal: " ++ pretty t  internaliseExp _ (E.FloatLit v (Info t) _) =   case t of     E.Scalar (E.Prim (E.FloatType ft)) ->       return [I.Constant $ I.FloatValue $ floatValue ft v]-    _ -> fail $ "internaliseExp: nonsensical type for float literal: " ++ pretty t+    _ -> error $ "internaliseExp: nonsensical type for float literal: " ++ pretty t -internaliseExp desc (E.If ce te fe _ _) =-  letTupExp' desc =<< eIf (BasicOp . SubExp <$> internaliseExp1 "cond" ce)-                          (internaliseBody te) (internaliseBody fe)+internaliseExp desc (E.If ce te fe (Info ret, Info retext) _) = do+  ses <- letTupExp' desc =<<+         eIf (BasicOp . SubExp <$> internaliseExp1 "cond" ce)+         (internaliseBody te) (internaliseBody fe)+  bindExtSizes (E.toStruct ret) retext ses+  return ses  -- Builtin operators are handled specially because they are -- overloaded.-internaliseExp desc (E.BinOp (op, _) _ (xe,_) (ye,_) _ loc)+internaliseExp desc (E.BinOp (op, _) _ (xe,_) (ye,_) _ _ loc)   | Just internalise <- isOverloadedFunction op [xe, ye] loc =       internalise desc  -- User-defined operators are just the same as a function call.-internaliseExp desc (E.BinOp (op, oploc) (Info t) (xarg, Info xt) (yarg, Info yt) _ loc) =+internaliseExp desc (E.BinOp (op, oploc) (Info t)+                     (xarg, Info (xt,xext)) (yarg, Info (yt,yext))+                     _ (Info retext) loc) =   internaliseExp desc $-  E.Apply (E.Apply (E.Var op (Info t) oploc) xarg (Info $ E.diet xt)-           (Info $ foldFunType [E.fromStruct yt] t) loc)-          yarg (Info $ E.diet yt) (Info t) loc+  E.Apply (E.Apply (E.Var op (Info t) oploc) xarg (Info (E.diet xt, xext))+           (Info $ foldFunType [E.fromStruct yt] t, Info []) loc)+          yarg (Info (E.diet yt, yext)) (Info t, Info retext) loc  internaliseExp desc (E.Project k e (Info rt) _) = do   n <- internalisedTypeSize $ rt `setAliases` ()@@ -798,23 +851,31 @@   take n . drop i' <$> internaliseExp desc e  internaliseExp _ e@E.Lambda{} =-  fail $ "internaliseExp: Unexpected lambda at " ++ locStr (srclocOf e)+  error $ "internaliseExp: Unexpected lambda at " ++ locStr (srclocOf e)  internaliseExp _ e@E.OpSection{} =-  fail $ "internaliseExp: Unexpected operator section at " ++ locStr (srclocOf e)+  error $ "internaliseExp: Unexpected operator section at " ++ locStr (srclocOf e)  internaliseExp _ e@E.OpSectionLeft{} =-  fail $ "internaliseExp: Unexpected left operator section at " ++ locStr (srclocOf e)+  error $ "internaliseExp: Unexpected left operator section at " ++ locStr (srclocOf e)  internaliseExp _ e@E.OpSectionRight{} =-  fail $ "internaliseExp: Unexpected right operator section at " ++ locStr (srclocOf e)+  error $ "internaliseExp: Unexpected right operator section at " ++ locStr (srclocOf e)  internaliseExp _ e@E.ProjectSection{} =-  fail $ "internaliseExp: Unexpected projection section at " ++ locStr (srclocOf e)+  error $ "internaliseExp: Unexpected projection section at " ++ locStr (srclocOf e)  internaliseExp _ e@E.IndexSection{} =-  fail $ "internaliseExp: Unexpected index section at " ++ locStr (srclocOf e)+  error $ "internaliseExp: Unexpected index section at " ++ locStr (srclocOf e) +internaliseArg :: String -> (E.Exp, Maybe VName) -> InternaliseM [SubExp]+internaliseArg desc (arg, argdim) = do+  arg' <- internaliseExp desc arg+  case (arg', argdim) of+    ([se], Just d) -> letBindNames_ [d] $ BasicOp $ SubExp se+    _ -> return ()+  return arg'+ generateCond :: E.Pattern -> [I.SubExp] -> InternaliseM (I.SubExp, [I.SubExp]) generateCond orig_p orig_ses = do   (cmps, pertinent, _) <- compares orig_p orig_ses@@ -824,7 +885,7 @@     -- Literals are always primitive values.     compares (E.PatternLit e _ _) (se:ses) = do       e' <- internaliseExp1 "constant" e-      I.Prim t' <- subExpType se+      t' <- I.elemType <$> subExpType se       cmp <- letSubExp "match_lit" $ I.BasicOp $ I.CmpOp (I.CmpEq t') e' se       return ([cmp], [se], ses) @@ -838,10 +899,10 @@           (cmps, pertinent, _) <- comparesMany pats $ map (payload_ses!!) payload_is           return (cmp : cmps, pertinent, ses')         Nothing ->-          fail "generateCond: missing constructor"+          error "generateCond: missing constructor"      compares (E.PatternConstr _ (Info t) _ _) _ =-      fail $ "generateCond: PatternConstr has nonsensical type: " ++ pretty t+      error $ "generateCond: PatternConstr has nonsensical type: " ++ pretty t      compares (E.Id _ t loc) ses =       compares (E.Wildcard t loc) ses@@ -1100,7 +1161,7 @@   outsz  <- arraysSize 0 <$> mapM lookupType arrs   let acc_arr_tps = [ I.arrayOf t (I.Shape [outsz]) NoUniqueness | t <- acctps ]   lam0'  <- internaliseFoldLambda internaliseLambda lam0 acctps acc_arr_tps-  let lam0_acc_params = fst $ splitAt (length accs) $ I.lambdaParams lam0'+  let lam0_acc_params = take (length accs) $ I.lambdaParams lam0'   acc_params <- forM lam0_acc_params $ \p -> do     name <- newVName $ baseString $ I.paramName p     return p { I.paramName = name }@@ -1135,7 +1196,7 @@ internaliseExp1 desc e = do   vs <- internaliseExp desc e   case vs of [se] -> return se-             _ -> fail "Internalise.internaliseExp1: was passed not just a single subexpression"+             _ -> error "Internalise.internaliseExp1: was passed not just a single subexpression"  -- | Promote to dimension type as appropriate for the original type. -- Also return original type.@@ -1145,7 +1206,7 @@   case E.typeOf e of     E.Scalar (E.Prim (Signed it))   -> (,it) <$> asIntS Int32 e'     E.Scalar (E.Prim (Unsigned it)) -> (,it) <$> asIntZ Int32 e'-    _                               -> fail "internaliseDimExp: bad type"+    _                               -> error "internaliseDimExp: bad type"  internaliseExpToVars :: String -> E.Exp -> InternaliseM [I.VName] internaliseExpToVars desc e =@@ -1161,119 +1222,151 @@   vs <- internaliseExpToVars s e   letSubExps s =<< mapM (fmap I.BasicOp . op) vs -internaliseBinOp :: String+certifyingNonzero :: SrcLoc -> IntType -> SubExp+                  -> InternaliseM a+                  -> InternaliseM a+certifyingNonzero loc t x m = do+  c <- assertingOne $ do+    zero <- letSubExp "zero" $ I.BasicOp $+            CmpOp (CmpEq (IntType t)) x (intConst t 0)+    nonzero <- letSubExp "nonzero" $ I.BasicOp $ UnOp Not zero+    letExp "nonzero_cert" $ I.BasicOp $+      I.Assert nonzero "division by zero" (loc, mempty)+  certifying c m++certifyingNonnegative :: SrcLoc -> IntType -> SubExp+                      -> InternaliseM a+                      -> InternaliseM a+certifyingNonnegative loc t x m = do+  c <- assertingOne $ do+    nonnegative <- letSubExp "nonnegative" $ I.BasicOp $+                   CmpOp (CmpSle t) (intConst t 0) x+    letExp "nonzero_cert" $ I.BasicOp $+      I.Assert nonnegative "negative exponent" (loc, mempty)+  certifying c m++internaliseBinOp :: SrcLoc -> String                  -> E.BinOp                  -> I.SubExp -> I.SubExp                  -> E.PrimType                  -> E.PrimType                  -> InternaliseM [I.SubExp]-internaliseBinOp desc E.Plus x y (E.Signed t) _ =+internaliseBinOp _ desc E.Plus x y (E.Signed t) _ =   simpleBinOp desc (I.Add t) x y-internaliseBinOp desc E.Plus x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Plus x y (E.Unsigned t) _ =   simpleBinOp desc (I.Add t) x y-internaliseBinOp desc E.Plus x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Plus x y (E.FloatType t) _ =   simpleBinOp desc (I.FAdd t) x y-internaliseBinOp desc E.Minus x y (E.Signed t) _ =+internaliseBinOp _ desc E.Minus x y (E.Signed t) _ =   simpleBinOp desc (I.Sub t) x y-internaliseBinOp desc E.Minus x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Minus x y (E.Unsigned t) _ =   simpleBinOp desc (I.Sub t) x y-internaliseBinOp desc E.Minus x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Minus x y (E.FloatType t) _ =   simpleBinOp desc (I.FSub t) x y-internaliseBinOp desc E.Times x y (E.Signed t) _ =+internaliseBinOp _ desc E.Times x y (E.Signed t) _ =   simpleBinOp desc (I.Mul t) x y-internaliseBinOp desc E.Times x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Times x y (E.Unsigned t) _ =   simpleBinOp desc (I.Mul t) x y-internaliseBinOp desc E.Times x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Times x y (E.FloatType t) _ =   simpleBinOp desc (I.FMul t) x y-internaliseBinOp desc E.Divide x y (E.Signed t) _ =+internaliseBinOp loc desc E.Divide x y (E.Signed t) _ =+  certifyingNonzero loc t y $   simpleBinOp desc (I.SDiv t) x y-internaliseBinOp desc E.Divide x y (E.Unsigned t) _ =+internaliseBinOp loc desc E.Divide x y (E.Unsigned t) _ =+  certifyingNonzero loc t y $   simpleBinOp desc (I.UDiv t) x y-internaliseBinOp desc E.Divide x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Divide x y (E.FloatType t) _ =   simpleBinOp desc (I.FDiv t) x y-internaliseBinOp desc E.Pow x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Pow x y (E.FloatType t) _ =   simpleBinOp desc (I.FPow t) x y-internaliseBinOp desc E.Pow x y (E.Signed t) _ =+internaliseBinOp loc desc E.Pow x y (E.Signed t) _ =+  certifyingNonnegative loc t y $   simpleBinOp desc (I.Pow t) x y-internaliseBinOp desc E.Pow x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Pow x y (E.Unsigned t) _ =   simpleBinOp desc (I.Pow t) x y-internaliseBinOp desc E.Mod x y (E.Signed t) _ =+internaliseBinOp loc desc E.Mod x y (E.Signed t) _ =+  certifyingNonzero loc t y $   simpleBinOp desc (I.SMod t) x y-internaliseBinOp desc E.Mod x y (E.Unsigned t) _ =+internaliseBinOp loc desc E.Mod x y (E.Unsigned t) _ =+  certifyingNonzero loc t y $   simpleBinOp desc (I.UMod t) x y-internaliseBinOp desc E.Mod x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Mod x y (E.FloatType t) _ =   simpleBinOp desc (I.FMod t) x y-internaliseBinOp desc E.Quot x y (E.Signed t) _ =+internaliseBinOp loc desc E.Quot x y (E.Signed t) _ =+  certifyingNonzero loc t y $   simpleBinOp desc (I.SQuot t) x y-internaliseBinOp desc E.Quot x y (E.Unsigned t) _ =+internaliseBinOp loc desc E.Quot x y (E.Unsigned t) _ =+  certifyingNonzero loc t y $   simpleBinOp desc (I.UDiv t) x y-internaliseBinOp desc E.Rem x y (E.Signed t) _ =+internaliseBinOp loc desc E.Rem x y (E.Signed t) _ =+  certifyingNonzero loc t y $   simpleBinOp desc (I.SRem t) x y-internaliseBinOp desc E.Rem x y (E.Unsigned t) _ =+internaliseBinOp loc desc E.Rem x y (E.Unsigned t) _ =+  certifyingNonzero loc t y $   simpleBinOp desc (I.UMod t) x y-internaliseBinOp desc E.ShiftR x y (E.Signed t) _ =+internaliseBinOp _ desc E.ShiftR x y (E.Signed t) _ =   simpleBinOp desc (I.AShr t) x y-internaliseBinOp desc E.ShiftR x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.ShiftR x y (E.Unsigned t) _ =   simpleBinOp desc (I.LShr t) x y-internaliseBinOp desc E.ShiftL x y (E.Signed t) _ =+internaliseBinOp _ desc E.ShiftL x y (E.Signed t) _ =   simpleBinOp desc (I.Shl t) x y-internaliseBinOp desc E.ShiftL x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.ShiftL x y (E.Unsigned t) _ =   simpleBinOp desc (I.Shl t) x y-internaliseBinOp desc E.Band x y (E.Signed t) _ =+internaliseBinOp _ desc E.Band x y (E.Signed t) _ =   simpleBinOp desc (I.And t) x y-internaliseBinOp desc E.Band x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Band x y (E.Unsigned t) _ =   simpleBinOp desc (I.And t) x y-internaliseBinOp desc E.Xor x y (E.Signed t) _ =+internaliseBinOp _ desc E.Xor x y (E.Signed t) _ =   simpleBinOp desc (I.Xor t) x y-internaliseBinOp desc E.Xor x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Xor x y (E.Unsigned t) _ =   simpleBinOp desc (I.Xor t) x y-internaliseBinOp desc E.Bor x y (E.Signed t) _ =+internaliseBinOp _ desc E.Bor x y (E.Signed t) _ =   simpleBinOp desc (I.Or t) x y-internaliseBinOp desc E.Bor x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Bor x y (E.Unsigned t) _ =   simpleBinOp desc (I.Or t) x y -internaliseBinOp desc E.Equal x y t _ =+internaliseBinOp _ desc E.Equal x y t _ =   simpleCmpOp desc (I.CmpEq $ internalisePrimType t) x y-internaliseBinOp desc E.NotEqual x y t _ = do+internaliseBinOp _ desc E.NotEqual x y t _ = do   eq <- letSubExp (desc++"true") $ I.BasicOp $ I.CmpOp (I.CmpEq $ internalisePrimType t) x y   fmap pure $ letSubExp desc $ I.BasicOp $ I.UnOp I.Not eq-internaliseBinOp desc E.Less x y (E.Signed t) _ =+internaliseBinOp _ desc E.Less x y (E.Signed t) _ =   simpleCmpOp desc (I.CmpSlt t) x y-internaliseBinOp desc E.Less x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Less x y (E.Unsigned t) _ =   simpleCmpOp desc (I.CmpUlt t) x y-internaliseBinOp desc E.Leq x y (E.Signed t) _ =+internaliseBinOp _ desc E.Leq x y (E.Signed t) _ =   simpleCmpOp desc (I.CmpSle t) x y-internaliseBinOp desc E.Leq x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Leq x y (E.Unsigned t) _ =   simpleCmpOp desc (I.CmpUle t) x y-internaliseBinOp desc E.Greater x y (E.Signed t) _ =+internaliseBinOp _ desc E.Greater x y (E.Signed t) _ =   simpleCmpOp desc (I.CmpSlt t) y x -- Note the swapped x and y-internaliseBinOp desc E.Greater x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Greater x y (E.Unsigned t) _ =   simpleCmpOp desc (I.CmpUlt t) y x -- Note the swapped x and y-internaliseBinOp desc E.Geq x y (E.Signed t) _ =+internaliseBinOp _ desc E.Geq x y (E.Signed t) _ =   simpleCmpOp desc (I.CmpSle t) y x -- Note the swapped x and y-internaliseBinOp desc E.Geq x y (E.Unsigned t) _ =+internaliseBinOp _ desc E.Geq x y (E.Unsigned t) _ =   simpleCmpOp desc (I.CmpUle t) y x -- Note the swapped x and y-internaliseBinOp desc E.Less x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Less x y (E.FloatType t) _ =   simpleCmpOp desc (I.FCmpLt t) x y-internaliseBinOp desc E.Leq x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Leq x y (E.FloatType t) _ =   simpleCmpOp desc (I.FCmpLe t) x y-internaliseBinOp desc E.Greater x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Greater x y (E.FloatType t) _ =   simpleCmpOp desc (I.FCmpLt t) y x -- Note the swapped x and y-internaliseBinOp desc E.Geq x y (E.FloatType t) _ =+internaliseBinOp _ desc E.Geq x y (E.FloatType t) _ =   simpleCmpOp desc (I.FCmpLe t) y x -- Note the swapped x and y  -- Relational operators for booleans.-internaliseBinOp desc E.Less x y E.Bool _ =+internaliseBinOp _ desc E.Less x y E.Bool _ =   simpleCmpOp desc I.CmpLlt x y-internaliseBinOp desc E.Leq x y E.Bool _ =+internaliseBinOp _ desc E.Leq x y E.Bool _ =   simpleCmpOp desc I.CmpLle x y-internaliseBinOp desc E.Greater x y E.Bool _ =+internaliseBinOp _ desc E.Greater x y E.Bool _ =   simpleCmpOp desc I.CmpLlt y x -- Note the swapped x and y-internaliseBinOp desc E.Geq x y E.Bool _ =+internaliseBinOp _ desc E.Geq x y E.Bool _ =   simpleCmpOp desc I.CmpLle y x -- Note the swapped x and y -internaliseBinOp _ op _ _ t1 t2 =-  fail $ "Invalid binary operator " ++ pretty op +++internaliseBinOp _ _ op _ _ t1 t2 =+  error $ "Invalid binary operator " ++ pretty op ++   " with operand types " ++ pretty t1 ++ ", " ++ pretty t2  simpleBinOp :: String@@ -1290,14 +1383,17 @@ simpleCmpOp desc op x y =   letTupExp' desc $ I.BasicOp $ I.CmpOp op x y -findFuncall :: E.Exp -> InternaliseM (E.QualName VName, [E.Exp])-findFuncall (E.Var fname _ _) =-  return (fname, [])-findFuncall (E.Apply f arg _ _ _) = do-  (fname, args) <- findFuncall f-  return (fname, args ++ [arg])+findFuncall :: E.Exp -> InternaliseM (E.QualName VName,+                                      [(E.Exp, Maybe VName)],+                                      E.StructType,+                                      [VName])+findFuncall (E.Var fname (Info t) _) =+  return (fname, [], E.toStruct t, [])+findFuncall (E.Apply f arg (Info (_, argext)) (Info ret, Info retext) _) = do+  (fname, args, _, _) <- findFuncall f+  return (fname, args ++ [(arg, argext)], E.toStruct ret, retext) findFuncall e =-  fail $ "Invalid function expression in application: " ++ pretty e+  error $ "Invalid function expression in application: " ++ pretty e  internaliseLambda :: InternaliseLambda @@ -1311,7 +1407,7 @@     mapM_ (uncurry (internaliseDimConstant loc)) $ pcm<>rcm     return (params', body', map I.fromDecl rettype') -internaliseLambda e _ = fail $ "internaliseLambda: unexpected expression:\n" ++ pretty e+internaliseLambda e _ = error $ "internaliseLambda: unexpected expression:\n" ++ pretty e  internaliseDimConstant :: SrcLoc -> Name -> VName -> InternaliseM () internaliseDimConstant loc fname name =@@ -1363,10 +1459,10 @@     -- Short-circuiting operators are magical.     handle [x,y] "&&" = Just $ \desc ->       internaliseExp desc $-      E.If x y (E.Literal (E.BoolValue False) noLoc) (Info $ E.Scalar $ E.Prim E.Bool) noLoc+      E.If x y (E.Literal (E.BoolValue False) noLoc) (Info $ E.Scalar $ E.Prim E.Bool, Info []) noLoc     handle [x,y] "||" = Just $ \desc ->         internaliseExp desc $-        E.If x (E.Literal (E.BoolValue True) noLoc) y (Info $ E.Scalar $ E.Prim E.Bool) noLoc+        E.If x (E.Literal (E.BoolValue True) noLoc) y (Info $ E.Scalar $ E.Prim E.Bool, Info []) noLoc      -- Handle equality and inequality specially, to treat the case of     -- arrays.@@ -1425,8 +1521,8 @@         y' <- internaliseExp1 "y" y         case (E.typeOf x, E.typeOf y) of           (E.Scalar (E.Prim t1), E.Scalar (E.Prim t2)) ->-            internaliseBinOp desc bop x' y' t1 t2-          _ -> fail "Futhark.Internalise.internaliseExp: non-primitive type in BinOp."+            internaliseBinOp loc desc bop x' y' t1 t2+          _ -> error "Futhark.Internalise.internaliseExp: non-primitive type in BinOp."      handle [E.TupLit [a, si, v] _] "scatter" = Just $ scatterF a si v @@ -1568,7 +1664,7 @@           letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e'         E.Scalar (E.Prim (E.FloatType float_from)) ->           letTupExp' desc $ I.BasicOp $ I.ConvOp (I.FPToSI float_from int_to) e'-        _ -> fail "Futhark.Internalise.handle: non-numeric type in ToSigned"+        _ -> error "Futhark.Internalise.handle: non-numeric type in ToSigned"      toUnsigned int_to e desc = do       e' <- internaliseExp1 "trunc_arg" e@@ -1583,7 +1679,7 @@           letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e'         E.Scalar (E.Prim (E.FloatType float_from)) ->           letTupExp' desc $ I.BasicOp $ I.ConvOp (I.FPToUI float_from int_to) e'-        _ -> fail "Futhark.Internalise.internaliseExp: non-numeric type in ToUnsigned"+        _ -> error "Futhark.Internalise.internaliseExp: non-numeric type in ToUnsigned"      complementF e desc = do       e' <- internaliseExp1 "complement_arg" e@@ -1593,7 +1689,7 @@                  I.Prim I.Bool ->                    letTupExp' desc $ I.BasicOp $ I.UnOp I.Not e'                  _ ->-                   fail "Futhark.Internalise.internaliseExp: non-int/bool type in Complement"+                   error "Futhark.Internalise.internaliseExp: non-int/bool type in Complement"      scatterF a si v desc = do       si' <- letExp "write_si" . BasicOp . SubExp =<< internaliseExp1 "write_arg_i" si@@ -1653,17 +1749,19 @@   is_const <- lookupConst name   scope <- askScope   case is_const of-    Just (fname, constparams, mk_rettype)+    Just (fname, constparams, mk_rettype, bind_ext, _)       | name `M.notMember` scope -> do       (constargs, const_ds, const_ts) <- unzip3 <$> constFunctionArgs loc constparams       safety <- askSafety       case mk_rettype $ zip constargs $ map I.fromDecl const_ts of-        Nothing -> fail $ "internaliseIfConst: " +++        Nothing -> error $ "internaliseIfConst: " ++                    unwords (pretty name : zipWith (curry pretty) constargs const_ts) ++                    " failed"-        Just rettype ->-          fmap (Just . map I.Var) $ letTupExp (baseString name) $-          I.Apply fname (zip constargs const_ds) rettype (safety, loc, mempty)+        Just rettype -> do+          ses <- fmap (map I.Var) $ letTupExp (baseString name) $+                 I.Apply fname (zip constargs const_ds) rettype (safety, loc, mempty)+          bind_ext ses+          return $ Just ses     _ -> return Nothing  constFunctionArgs :: SrcLoc -> ConstParams -> InternaliseM [(SubExp, I.Diet, I.DeclType)]@@ -1682,8 +1780,8 @@   (constargs, const_ds, _) <- unzip3 <$> constFunctionArgs loc constparams   argts <- mapM subExpType args   closure_ts <- mapM lookupType closure-  shapeargs <- argShapes shapes value_paramts argts-  let diets = const_ds ++ replicate (length closure + length shapeargs) I.ObservePrim +++  let shapeargs = argShapes shapes value_paramts argts+      diets = const_ds ++ replicate (length closure + length shapeargs) I.ObservePrim ++               map I.diet value_paramts       constOrShape = const $ I.Prim int32       paramts = map constOrShape constargs ++ closure_ts ++@@ -1693,7 +1791,7 @@            paramts (constargs ++ map I.Var closure ++ shapeargs ++ args)   argts' <- mapM subExpType args'   case rettype_fun $ zip args' argts' of-    Nothing -> fail $ "Cannot apply " ++ pretty fname ++ " to arguments\n " +++    Nothing -> error $ "Cannot apply " ++ pretty fname ++ " to arguments\n " ++                pretty args' ++ "\nof types\n " ++                pretty argts' ++                "\nFunction has parameters\n " ++ pretty fun_params@@ -1702,10 +1800,29 @@       ses <- letTupExp' desc $ I.Apply fname' (zip args' diets) ts (safety, loc, mempty)       return (ses, map I.fromDecl ts) +-- Bind existential names defined by an expression, based on the+-- concrete values that expression evaluated to.  This most+-- importantly should be done after function calls, but also+-- everything else that can produce existentials in the source+-- language.+bindExtSizes :: E.StructType -> [VName] -> [SubExp] -> InternaliseM ()+bindExtSizes ret retext ses = do+  ts <- concat . fst <$>+        internaliseParamTypes mempty (M.fromList $ zip retext retext) [ret]+  ses_ts <- mapM subExpType ses++  let combine t1 t2 =+        mconcat $ zipWith combine' (arrayExtDims t1) (arrayDims t2)+      combine' (I.Free (I.Var v)) se+        | v `elem` retext = M.singleton v se+      combine' _ _ = mempty++  forM_ (M.toList $ mconcat $ zipWith combine ts ses_ts) $ \(v, se) ->+    letBindNames_ [v] $ BasicOp $ SubExp se+ askSafety :: InternaliseM Safety askSafety = do check <- asks envDoBoundsChecks-               safe <- asks envSafe-               return $ if check || safe then I.Safe else I.Unsafe+               return $ if check then I.Safe else I.Unsafe  -- Implement partitioning using maps, scans and writes. partitionWithSOACS :: Int -> I.Lambda -> [I.VName] -> InternaliseM ([I.SubExp], [I.SubExp])@@ -1715,7 +1832,7 @@   classes_and_increments <- letTupExp "increments" $ I.Op $ I.Screma w (mapSOAC lam) arrs   (classes, increments) <- case classes_and_increments of                              classes : increments -> return (classes, take k increments)-                             _                    -> fail "partitionWithSOACS"+                             _                    -> error "partitionWithSOACS"    add_lam_x_params <-     replicateM k $ I.Param <$> newVName "x" <*> pure (I.Prim int32)
src/Futhark/Internalise/AccurateSizes.hs view
@@ -27,7 +27,7 @@   runBodyBinder $ do     ses <- bodyBind body     sets <- mapM subExpType ses-    resultBody <$> argShapes shapenames ts sets+    resultBodyM $ argShapes shapenames ts sets  annotateArrayShape :: ArrayShape shape =>                       TypeBase shape u -> [Int] -> TypeBase Shape u@@ -35,25 +35,14 @@   t `setArrayShape` Shape (take (arrayRank t) $                            map (intConst Int32 . toInteger) $ newshape ++ repeat 0) --- Some trickery is needed here to predict sensible values for--- dimensions that are used exclusively as the inner dimension of an--- array.  The issue is that the dimension may be inside an empty--- array.  In this case, the dimension inside the empty array should--- not count, as it will be zero.  The solution we use is to take the--- maximum of such sizes; this will effectively disregard the zeroes.-argShapes :: MonadBinder m =>-             [VName] -> [TypeBase Shape u0] -> [TypeBase Shape u1] -> m [SubExp]+argShapes :: [VName] -> [TypeBase Shape u0] -> [TypeBase Shape u1] -> [SubExp] argShapes shapes valts valargts =-  mapM addShape shapes+  map addShape shapes   where mapping = shapeMapping valts valargts-        outer_dims = map (arraySize 0) valts         addShape name =           case M.lookup name mapping of-            Just s | x:xs <- S.toList s ->-                       if Var name `elem` outer_dims-                       then return x-                       else letSubExp (baseString name) =<< foldBinOp (SMax Int32) x xs-            _ -> return $ intConst Int32 0+            Just s | se:_ <- S.toList s -> se+            _ -> intConst Int32 0  ensureResultShape :: MonadBinder m =>                      (m Certificates -> m Certificates)@@ -119,7 +108,6 @@           | otherwise =               ensureShape asserting msg loc t (baseString v) $ Var v - ensureShapeVar :: MonadBinder m =>                   (m Certificates -> m Certificates)                -> ErrorMsg SubExp -> SrcLoc -> ExtType -> String -> VName@@ -132,20 +120,11 @@       then return v       else do         certs <- asserting $ do-          old_zero <- letSubExp "old_empty" =<< anyZero olddims-          new_zero <- letSubExp "new_empty" =<< anyZero newdims-          both_empty <- letSubExp "both_empty" $ BasicOp $ BinOp LogAnd old_zero new_zero-           matches <- zipWithM checkDim newdims olddims           all_match <- letSubExp "match" =<< foldBinOp LogAnd (constant True) matches--          empty_or_match <- letSubExp "empty_or_match" $ BasicOp $ BinOp LogOr both_empty all_match           Certificates . pure <$> letExp "empty_or_match_cert"-            (BasicOp $ Assert empty_or_match msg (loc, []))+            (BasicOp $ Assert all_match msg (loc, []))         certifying certs $ letExp name $ shapeCoerce newdims v   | otherwise = return v   where checkDim desired has =           letSubExp "dim_match" $ BasicOp $ CmpOp (CmpEq int32) desired has-        anyZero =-          foldBinOp LogOr (constant False) <=<-          mapM (letSubExp "dim_zero" . BasicOp . CmpOp (CmpEq int32) (intConst Int32 0))
src/Futhark/Internalise/Bindings.hs view
@@ -17,7 +17,7 @@ import qualified Data.Set as S import Data.Loc -import Language.Futhark as E+import Language.Futhark as E hiding (matchDims) import qualified Futhark.Representation.SOACS as I import Futhark.MonadFreshNames import Futhark.Internalise.Monad@@ -129,6 +129,11 @@           new_name <- newVName $ baseString v           return [((E.Ident v (Info t) loc, new_name),                    t `E.setAliases` ())]+        -- XXX: treat empty tuples and records as bool.+        flattenPattern' (E.TuplePattern [] loc) =+          flattenPattern' (E.Wildcard (Info $ E.Scalar $ E.Prim E.Bool) loc)+        flattenPattern' (E.RecordPattern [] loc) =+          flattenPattern' (E.Wildcard (Info $ E.Scalar $ E.Prim E.Bool) loc)         flattenPattern' (E.TuplePattern pats _) =           concat <$> mapM flattenPattern' pats         flattenPattern' (E.RecordPattern fs loc) =
src/Futhark/Internalise/Defunctionalise.hs view
@@ -1,9 +1,11 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleContexts #-} -- | Defunctionalization of typed, monomorphic Futhark programs without modules. module Futhark.Internalise.Defunctionalise   ( transformProg ) where  import qualified Control.Arrow as Arrow+import           Control.Monad.State import           Control.Monad.RWS hiding (Sum) import           Data.Bifunctor import           Data.Foldable@@ -111,9 +113,39 @@     Nothing -- If the variable is unknown, it may refer to the 'intrinsics'             -- module, which we will have to treat specially.       | baseTag x <= maxIntrinsicTag -> return IntrinsicSV-      | otherwise -> error $ "Variable " ++ pretty x ++ " at "+      | otherwise -> -- Anything not in scope is going to be an+                     -- existential size.+          return $ Dynamic $ Scalar $ Prim $ Signed Int32+      | otherwise ->  error $ "Variable " ++ pretty x ++ " at "                           ++ locStr loc ++ " is out of scope." +-- Like patternDimNames, but ignores sizes that are only found in+-- funtion types.+arraySizes :: StructType -> S.Set VName+arraySizes (Scalar Arrow{}) = mempty+arraySizes (Scalar (Record fields)) = foldMap arraySizes fields+arraySizes (Scalar (Sum cs)) = foldMap (foldMap arraySizes) cs+arraySizes (Scalar TypeVar{}) = mempty+arraySizes (Scalar Prim{}) = mempty+arraySizes (Array _ _ t shape) =+  arraySizes (Scalar t) <> foldMap dimName (shapeDims shape)+  where dimName :: DimDecl VName -> S.Set VName+        dimName (NamedDim qn) = S.singleton $ qualLeaf qn+        dimName _             = mempty++patternArraySizes :: Pattern -> S.Set VName+patternArraySizes = arraySizes . patternStructType++dimMapping :: Monoid a =>+              TypeBase (DimDecl VName) a+           -> TypeBase (DimDecl VName) a+           -> M.Map VName VName+dimMapping t1 t2 = execState (matchDims f t1 t2) mempty+  where f (NamedDim d1) (NamedDim d2) = do+          modify $ M.insert (qualLeaf d1) (qualLeaf d2)+          return $ NamedDim d1+        f d _ = return d+ defuncFun :: [TypeParam] -> [Pattern] -> Exp -> (Aliasing, StructType) -> SrcLoc           -> DefM (Exp, StaticVal) defuncFun tparams pats e0 (closure, ret) loc = do@@ -129,7 +161,7 @@           -- Split shape parameters into those that are determined by           -- the first pattern, and those that are determined by later           -- patterns.-          let bound_by_pat = (`S.member` patternDimNames pat') . typeParamName+          let bound_by_pat = (`S.member` patternArraySizes pat') . typeParamName               (pat_dims, rest_dims) = partition bound_by_pat tparams           in (map typeParamName pat_dims, pat',               foldFunType (map (toStruct . patternType) pats') ret,@@ -139,20 +171,21 @@   -- the lambda.  Closed-over 'DynamicFun's are converted to their   -- closure representation.   let used = freeVars (Lambda pats e0 Nothing (Info (closure, ret)) loc)-             `without` mconcat (map (oneName . typeParamName) tparams)-  env <- restrictEnvTo used+             `without` mconcat (map oneName dims)+  used_env <- restrictEnvTo used -  let (fields, env') = unzip $ map closureFromDynamicFun $ M.toList env-      env'' = M.fromList env'-      dimIfNotInClosure v = do-        guard $ v `M.notMember` env''-        guard $ v `notElem` dims-        return v-      closure_dims =-        mapMaybe dimIfNotInClosure $ S.toList $ patternDimNames pat+  -- The closure parts that are sizes are proactively turned into size+  -- parameters.+  let sizes_of_arrays = foldMap (arraySizes . toStruct . typeFromSV) used_env <>+                        patternArraySizes pat+      notSize = not . (`S.member` sizes_of_arrays)+      (fields, env) = unzip $ map closureFromDynamicFun $+                      filter (notSize . fst) $ M.toList used_env+      env' = M.fromList env+      closure_dims = S.toList sizes_of_arrays    return (RecordLit fields loc,-          LambdaSV (dims++closure_dims) pat ret' e0' env'')+          LambdaSV (nub $ dims<>closure_dims) pat ret' e0' env')    where closureFromDynamicFun (vn, DynamicFun (clsr_env, sv) _) =           let name = nameFromString $ pretty vn@@ -217,7 +250,7 @@   es' <- mapM defuncExp' es   return (ArrayLit es' t loc, Dynamic t') -defuncExp (Range e1 me incl t@(Info t') loc) = do+defuncExp (Range e1 me incl t@(Info t', _) loc) = do   e1' <- defuncExp' e1   me' <- mapM defuncExp' me   incl' <- mapM defuncExp' incl@@ -233,29 +266,40 @@     -- Intrinsic functions used as variables are eta-expanded, so we     -- can get rid of them.     IntrinsicSV -> do-      (pats, body, tp) <- etaExpand e+      (pats, body, tp) <- etaExpand (typeOf e) e       defuncExp $ Lambda pats body Nothing (Info (mempty, tp)) noLoc     _ -> let tp = typeFromSV sv          in return (Var qn (Info tp) loc, sv) -defuncExp (Ascript e0 tydecl t loc)+defuncExp (Ascript e0 tydecl loc)   | orderZero (typeOf e0) = do (e0', sv) <- defuncExp e0-                               return (Ascript e0' tydecl t loc, sv)+                               return (Ascript e0' tydecl loc, sv)   | otherwise = defuncExp e0 -defuncExp (LetPat pat e1 e2 _ loc) = do+defuncExp (Coerce e0 tydecl t loc)+  | orderZero (typeOf e0) = do (e0', sv) <- defuncExp e0+                               return (Coerce e0' tydecl t loc, sv)+  | otherwise = defuncExp e0++defuncExp (LetPat pat e1 e2 (Info t, retext) loc) = do   (e1', sv1) <- defuncExp e1   let env  = matchPatternSV pat sv1       pat' = updatePattern pat sv1   (e2', sv2) <- localEnv env $ defuncExp e2-  return (LetPat pat' e1' e2' (Info $ typeOf e2') loc, sv2)+  -- To maintain any sizes going out of scope, we need to compute the+  -- old size substitution induced by retext and also apply it to the+  -- newly computed body type.+  let mapping = dimMapping (typeOf e2) t+      subst v = fromMaybe v $ M.lookup v mapping+      t' = first (fmap subst) $ typeOf e2'+  return (LetPat pat' e1' e2' (Info t', retext) loc, sv2)  -- Local functions are handled by rewriting them to lambdas, so that -- the same machinery can be re-used.-defuncExp (LetFun vn (dims, pats, _, Info ret, e1) e2 loc) = do+defuncExp (LetFun vn (dims, pats, _, Info ret, e1) e2 _ loc) = do   (e1', sv1) <- defuncFun dims pats e1 (mempty, ret) loc   (e2', sv2) <- localEnv (M.singleton vn sv1) $ defuncExp e2-  return (LetPat (Id vn (Info (typeOf e1')) loc) e1' e2' (Info $ typeOf e2') loc,+  return (LetPat (Id vn (Info (typeOf e1')) loc) e1' e2' (Info $ typeOf e2', Info []) loc,           sv2)  defuncExp (If e1 e2 e3 tp loc) = do@@ -264,12 +308,12 @@   (e3', _ ) <- defuncExp e3   return (If e1' e2' e3' tp loc, sv) -defuncExp e@(Apply f@(Var f' _ _) arg d t loc)+defuncExp e@(Apply f@(Var f' _ _) arg d (t, ext) loc)   | baseTag (qualLeaf f') <= maxIntrinsicTag,     TupLit es tuploc <- arg = do       -- defuncSoacExp also works fine for non-SOACs.       es' <- mapM defuncSoacExp es-      return (Apply f (TupLit es' tuploc) d t loc,+      return (Apply f (TupLit es' tuploc) d (t, ext) loc,               Dynamic $ typeOf e)  defuncExp e@Apply{} = defuncApply 0 e@@ -289,7 +333,7 @@ defuncExp ProjectSection{} = error "defuncExp: unexpected projection section." defuncExp IndexSection{}   = error "defuncExp: unexpected projection section." -defuncExp (DoLoop pat e1 form e3 loc) = do+defuncExp (DoLoop sparams pat e1 form e3 ret loc) = do   (e1', sv1) <- defuncExp e1   let env1 = matchPatternSV pat sv1   (form', env2) <- case form of@@ -300,15 +344,18 @@     While e2      -> do e2' <- localEnv env1 $ defuncExp' e2                         return (While e2', mempty)   (e3', sv) <- localEnv (env1 <> env2) $ defuncExp e3-  return (DoLoop pat e1' form' e3' loc, sv)+  return (DoLoop sparams pat e1' form' e3' ret loc, sv)   where envFromIdent (Ident vn (Info tp) _) =           M.singleton vn $ Dynamic tp  -- We handle BinOps by turning them into ordinary function applications.-defuncExp (BinOp (qn, qnloc) (Info t) (e1, Info pt1) (e2, Info pt2) (Info ret) loc) =+defuncExp (BinOp (qn, qnloc) (Info t)+           (e1, Info (pt1, ext1)) (e2, Info (pt2, ext2))+           (Info ret) (Info retext) loc) =   defuncExp $ Apply (Apply (Var qn (Info t) qnloc)-                     e1 (Info (diet pt1)) (Info (Scalar $ Arrow mempty Unnamed (fromStruct pt2) ret)) loc)-                    e2 (Info (diet pt2)) (Info ret) loc+                     e1 (Info (diet pt1, ext1))+                     (Info (Scalar $ Arrow mempty Unnamed (fromStruct pt2) ret), Info []) loc)+                    e2 (Info (diet pt2, ext2)) (Info ret, Info retext) loc  defuncExp (Project vn e0 tp@(Info tp') loc) = do   (e0', sv0) <- defuncExp e0@@ -429,27 +476,28 @@  defuncSoacExp e   | Scalar Arrow{} <- typeOf e = do-      (pats, body, tp) <- etaExpand e+      (pats, body, tp) <- etaExpand (typeOf e) e       let env = foldMap envFromPattern pats       body' <- localEnv env $ defuncExp' body       return $ Lambda pats body' Nothing (Info (mempty, tp)) noLoc   | otherwise = defuncExp' e -etaExpand :: Exp -> DefM ([Pattern], Exp, StructType)-etaExpand e = do-  let (ps, ret) = getType $ typeOf e-  (pats, vars) <- fmap unzip . forM ps $ \t -> do-    x <- newNameFromString "x"+etaExpand :: PatternType -> Exp -> DefM ([Pattern], Exp, StructType)+etaExpand e_t e = do+  let (ps, ret) = getType e_t+  (pats, vars) <- fmap unzip . forM ps $ \(p, t) -> do+    x <- case p of Named x -> pure x+                   Unnamed -> newNameFromString "x"     return (Id x (Info t) noLoc,             Var (qualName x) (Info t) noLoc)   let e' = foldl' (\e1 (e2, t2, argtypes) ->-                     Apply e1 e2 (Info $ diet t2)-                     (Info (foldFunType argtypes ret)) noLoc)-           e $ zip3 vars ps (drop 1 $ tails ps)+                     Apply e1 e2 (Info (diet t2, Nothing))+                     (Info (foldFunType argtypes ret), Info []) noLoc)+           e $ zip3 vars (map snd ps) (drop 1 $ tails $ map snd ps)   return (pats, e', toStruct ret) -  where getType (Scalar (Arrow _ _ t1 t2)) =-          let (ps, r) = getType t2 in (t1 : ps, r)+  where getType (Scalar (Arrow _ p t1 t2)) =+          let (ps, r) = getType t2 in ((p,t1) : ps, r)         getType t = ([], t)  -- | Defunctionalize an indexing of a single array dimension.@@ -461,25 +509,25 @@  -- | Defunctionalize a let-bound function, while preserving parameters -- that have order 0 types (i.e., non-functional).-defuncLet :: [TypeParam] -> [Pattern] -> Exp -> Info StructType+defuncLet :: [TypeParam] -> [Pattern] -> Exp -> StructType           -> DefM ([TypeParam], [Pattern], Exp, StaticVal)-defuncLet dims ps@(pat:pats) body (Info rettype)+defuncLet dims ps@(pat:pats) body rettype   | patternOrderZero pat = do        let bound_by_pat = (`S.member` patternDimNames pat) . typeParamName           -- Take care to not include more size parameters than necessary.           (pat_dims, rest_dims) = partition bound_by_pat dims           env = envFromPattern pat <> envFromShapeParams pat_dims-      (rest_dims', pats', body', sv) <- localEnv env $ defuncLet rest_dims pats body (Info rettype)+      (rest_dims', pats', body', sv) <- localEnv env $ defuncLet rest_dims pats body rettype       closure <- defuncFun dims ps body (mempty, rettype) noLoc       return (pat_dims ++ rest_dims', pat : pats', body', DynamicFun closure sv)   | otherwise = do       (e, sv) <- defuncFun dims ps body (mempty, rettype) noLoc       return ([], [], e, sv) -defuncLet _ [] body (Info rettype) = do+defuncLet _ [] body rettype = do   (body', sv) <- defuncExp body-  return ([], [], body', imposeType sv rettype )+  return ([], [], body', imposeType sv rettype)   where imposeType Dynamic{} t =           Dynamic $ fromStruct t         imposeType (RecordSV fs1) (Scalar (Record fs2)) =@@ -491,7 +539,7 @@ -- but a new lifted function is created if a dynamic function is only partially -- applied. defuncApply :: Int -> Exp -> DefM (Exp, StaticVal)-defuncApply depth e@(Apply e1 e2 d t@(Info ret) loc) = do+defuncApply depth e@(Apply e1 e2 d t@(Info ret, Info ext) loc) = do   let (argtypes, _) = unfoldFunType ret   (e1', sv1) <- defuncApply (depth+1) e1   (e2', sv2) <- defuncExp e2@@ -513,9 +561,14 @@           params_for_rettype = params ++ svParams sv1 ++ svParams sv2           svParams (LambdaSV _ sv_pat _ _ _) = [sv_pat]           svParams _                         = []-          rettype = buildRetType closure_env params_for_rettype e0_t $-                    anyDimShapeAnnotations $ typeOf e0'+          rettype = buildRetType closure_env params_for_rettype e0_t $ typeOf e0' +          already_bound = S.fromList dims <>+                          S.map identName (foldMap patternIdents params)+          more_dims = S.toList $+                      S.filter (`S.notMember` already_bound) $+                      foldMap patternArraySizes params+           -- Embed some information about the original function           -- into the name of the lifted function, to make the           -- result slightly more human-readable.@@ -524,24 +577,40 @@           liftedName i (Apply f _ _ _ _) =             liftedName (i+1) f           liftedName _ _ = "lifted"+       fname <- newNameFromString $ liftedName (0::Int) e1-      liftValDec fname rettype dims params e0'+      liftValDec fname rettype (dims ++ more_dims) params e0'        let t1 = toStruct $ typeOf e1'           t2 = toStruct $ typeOf e2'           fname' = qualName fname-      return (Parens (Apply (Apply (Var fname' (Info (Scalar $ Arrow mempty Unnamed (fromStruct t1) $-                                                      Scalar $ Arrow mempty Unnamed (fromStruct t2) rettype)) loc)-                             e1' (Info Observe) (Info $ Scalar $ Arrow mempty Unnamed (fromStruct t2) rettype) loc)-                      e2' d (Info rettype) loc) noLoc, sv)+          fname'' = Var fname' (Info (Scalar $ Arrow mempty Unnamed (fromStruct t1) $+                                      Scalar $ Arrow mempty Unnamed (fromStruct t2) rettype))+                    loc +          -- FIXME: what if this application returns both a function+          -- and a value?+          callret | orderZero ret = (Info ret, Info ext)+                  | otherwise     = (Info rettype, Info ext)++      return (Parens (Apply (Apply fname'' e1'+                              (Info (Observe, Nothing))+                              (Info $ Scalar $ Arrow mempty Unnamed (fromStruct t2) rettype,+                               Info [])+                              loc)+                      e2' d callret loc) noLoc, sv)+     -- If e1 is a dynamic function, we just leave the application in place,     -- but we update the types since it may be partially applied or return     -- a higher-order term.     DynamicFun _ sv ->       let (argtypes', rettype) = dynamicFunType sv argtypes-          apply_e = Apply e1' e2' d (Info $ foldFunType argtypes' rettype-                                    `setAliases` aliases ret) loc+          restype = foldFunType argtypes' rettype `setAliases` aliases ret+          -- FIXME: what if this application returns both a function+          -- and a value?+          callret | orderZero ret = (Info ret, Info ext)+                  | otherwise     = (Info restype, Info ext)+          apply_e = Apply e1' e2' d callret loc       in return (apply_e, sv)      -- Propagate the 'IntrinsicsSV' until we reach the outermost application,@@ -555,7 +624,7 @@           -- non-fully-applied intrinsic?           if null argtypes             then return (e', Dynamic $ typeOf e)-            else do (pats, body, tp) <- etaExpand e'+            else do (pats, body, tp) <- etaExpand (typeOf e') e'                     defuncExp $ Lambda pats body Nothing (Info (mempty, tp)) noLoc       | otherwise -> return (e', IntrinsicSV) @@ -576,8 +645,11 @@         | otherwise -> do             fname <- newName $ qualLeaf qn             let (dims, pats, e0, sv') = liftDynFun sv depth+                pats_names = S.map identName $ mconcat $ map patternIdents pats+                notInPats = (`S.notMember` pats_names)+                dims' = filter notInPats dims                 (argtypes', rettype) = dynamicFunType sv' argtypes-            liftValDec fname (fromStruct rettype) dims pats e0+            liftValDec fname (fromStruct rettype) dims' pats e0             return (Var (qualName fname)                     (Info (foldFunType argtypes' $ fromStruct rettype)) loc, sv') @@ -605,7 +677,7 @@ liftDynFun (DynamicFun (e, sv) _) 0 = ([], [], e, sv) liftDynFun (DynamicFun clsr@(_, LambdaSV dims pat _ _ _) sv) d   | d > 0 =  let (dims', pats, e', sv') = liftDynFun sv (d-1)-             in (dims ++ dims', pat : pats, e', DynamicFun clsr sv')+             in (nub $ dims ++ dims', pat : pats, e', DynamicFun clsr sv') liftDynFun sv _ = error $ "Tried to lift a StaticVal " ++ show sv                        ++ ", but expected a dynamic function." @@ -639,12 +711,22 @@ liftValDec :: VName -> PatternType -> [VName] -> [Pattern] -> Exp -> DefM () liftValDec fname rettype dims pats body = tell $ Seq.singleton dec   where dims' = map (flip TypeParamDim noLoc) dims-        rettype_st = anyDimShapeAnnotations $ toStruct rettype+        -- FIXME: this pass is still not correctly size-preserving, so+        -- forget those return sizes that we forgot to propagate along+        -- the way.  Hopefully the internaliser is conservative and+        -- will insert reshapes...+        bound_here = S.fromList dims <> S.map identName (foldMap patternIdents pats)+        anyDimIfNotBound (NamedDim v)+          | qualLeaf v `S.member` bound_here = NamedDim v+          | otherwise = AnyDim+        anyDimIfNotBound d = d+        rettype_st = first anyDimIfNotBound $ toStruct rettype+         dec = ValBind           { valBindEntryPoint = Nothing           , valBindName       = fname           , valBindRetDecl    = Nothing-          , valBindRetType    = Info rettype_st+          , valBindRetType    = Info (rettype_st, [])           , valBindTypeParams = dims'           , valBindParams     = pats           , valBindBody       = body@@ -656,8 +738,9 @@ -- binds the closed over variables. buildEnvPattern :: Env -> Pattern buildEnvPattern env = RecordPattern (map buildField $ M.toList env) noLoc-  where buildField (vn, sv) = (nameFromString (pretty vn),-                               Id vn (Info $ anyDimShapeAnnotations $ typeFromSV sv) noLoc)+  where buildField (vn, sv) =+          (nameFromString (pretty vn),+           Id vn (Info $ typeFromSV sv) noLoc)  -- | Given a closure environment pattern and the type of a term, -- construct the type of that term, where uniqueness is set to@@ -687,14 +770,17 @@  -- | Compute the corresponding type for a given static value. typeFromSV :: StaticVal -> PatternType-typeFromSV (Dynamic tp)           = anyDimShapeAnnotations tp-typeFromSV (LambdaSV _ _ _ _ env) = typeFromEnv env-typeFromSV (RecordSV ls)          = Scalar $ Record $ M.fromList $ map (fmap typeFromSV) ls-typeFromSV (DynamicFun (_, sv) _) = typeFromSV sv+typeFromSV (Dynamic tp) = tp+typeFromSV (LambdaSV sizes _ _ _ env) =+  unscopeType (S.fromList sizes) $ typeFromEnv env+typeFromSV (RecordSV ls) =+  Scalar $ Record $ M.fromList $ map (fmap typeFromSV) ls+typeFromSV (DynamicFun (_, sv) _) =+  typeFromSV sv typeFromSV (SumSV name svs fields) =   Scalar $ Sum $ M.insert name (map typeFromSV svs) $ M.fromList fields-typeFromSV IntrinsicSV            = error $ "Tried to get the type from the "-                                         ++ "static value of an intrinsic."+typeFromSV IntrinsicSV =+  error "Tried to get the type from the static value of an intrinsic."  typeFromEnv :: Env -> PatternType typeFromEnv = Scalar . Record . M.fromList .@@ -790,7 +876,7 @@ svFromType t                    = Dynamic t  -- A set of names where we also track uniqueness.-newtype NameSet = NameSet (M.Map VName Uniqueness)+newtype NameSet = NameSet (M.Map VName Uniqueness) deriving (Show)  instance Semigroup NameSet where   NameSet x <> NameSet y = NameSet $ M.unionWith max x y@@ -828,15 +914,17 @@     where freeVarsField (RecordFieldExplicit _ e _)  = freeVars e           freeVarsField (RecordFieldImplicit vn t _) = ident $ Ident vn t noLoc -  ArrayLit es _ _      -> foldMap freeVars es+  ArrayLit es t _      -> foldMap freeVars es <>+                          names (typeDimNames $ unInfo t)   Range e me incl _ _  -> freeVars e <> foldMap freeVars me <>                           foldMap freeVars incl   Var qn (Info t) _    -> NameSet $ M.singleton (qualLeaf qn) $ uniqueness t-  Ascript e t _ _      -> freeVars e <> names (typeDimNames $ unInfo $ expandedType t)+  Ascript e t _        -> freeVars e <> names (typeDimNames $ unInfo $ expandedType t)+  Coerce e t _ _       -> freeVars e <> names (typeDimNames $ unInfo $ expandedType t)   LetPat pat e1 e2 _ _ -> freeVars e1 <> ((names (patternDimNames pat) <> freeVars e2)                                           `without` patternVars pat) -  LetFun vn (_, pats, _, _, e1) e2 _ ->+  LetFun vn (_, pats, _, _, e1) e2 _ _ ->     ((freeVars e1 <> names (foldMap patternDimNames pats))       `without` foldMap patternVars pats) <>     (freeVars e2 `without` oneName vn)@@ -852,15 +940,17 @@   ProjectSection{}            -> mempty   IndexSection idxs _ _       -> foldMap freeDimIndex idxs -  DoLoop pat e1 form e3 _ -> let (e2fv, e2ident) = formVars form-                             in freeVars e1 <> e2fv <>-                             (freeVars e3 `without` (patternVars pat <> e2ident))+  DoLoop sparams pat e1 form e3 _ _ ->+    let (e2fv, e2ident) = formVars form+    in freeVars e1 <> e2fv <>+       (freeVars e3 `without`+        (names (S.fromList sparams) <> patternVars pat <> e2ident))     where formVars (For v e2) = (freeVars e2, ident v)           formVars (ForIn p e2)   = (freeVars e2, patternVars p)           formVars (While e2)     = (freeVars e2, mempty) -  BinOp (qn, _) _ (e1, _) (e2, _) _ _ -> oneName (qualLeaf qn) <>-                                         freeVars e1 <> freeVars e2+  BinOp (qn, _) _ (e1, _) (e2, _) _ _ _ -> oneName (qualLeaf qn) <>+                                           freeVars e1 <> freeVars e2   Project _ e _ _                -> freeVars e    LetWith id1 id2 idxs e1 e2 _ _ ->@@ -894,25 +984,21 @@ defuncValBind :: ValBind -> DefM (ValBind, Env, Bool)  -- Eta-expand entry points with a functional return type.-defuncValBind (ValBind entry@Just{} name _ (Info rettype) tparams params body _ loc)-  | (rettype_ps, rettype') <- unfoldFunType rettype,-    not $ null rettype_ps = do-      (body_pats, body', _) <- etaExpand body+defuncValBind (ValBind entry name _ (Info (rettype, retext)) tparams params body _ loc)+  | Scalar Arrow{} <- rettype = do+      (body_pats, body', rettype') <- etaExpand (fromStruct rettype) body       -- FIXME: we should also handle non-constant size annotations       -- here.       defuncValBind $ ValBind entry name Nothing-        (Info $ onlyConstantDims rettype')+        (Info (rettype', retext))         tparams (params <> body_pats) body' Nothing loc-  where onlyConstantDims = first onDim-        onDim (ConstDim x) = ConstDim x-        onDim _            = AnyDim -defuncValBind valbind@(ValBind _ name retdecl rettype tparams params body _ _) = do+defuncValBind valbind@(ValBind _ name retdecl (Info (rettype, retext)) tparams params body _ _) = do   (tparams', params', body', sv) <- defuncLet tparams params body rettype-  let rettype' = anyDimShapeAnnotations $ toStruct $ typeOf body'+  let rettype' = anySizes $ toStruct $ typeOf body'   return ( valbind { valBindRetDecl    = retdecl-                   , valBindRetType    = Info $ combineTypeShapes-                                         (unInfo rettype) rettype'+                   , valBindRetType    = Info (combineTypeShapes rettype rettype',+                                               retext)                    , valBindTypeParams = tparams'                    , valBindParams     = params'                    , valBindBody       = body'
src/Futhark/Internalise/Defunctorise.hs view
@@ -230,14 +230,14 @@ transformExp = transformNames  transformValBind :: ValBind -> TransformM ()-transformValBind (ValBind entry name tdecl (Info t) tparams params e doc loc) = do+transformValBind (ValBind entry name tdecl (Info (t, retext)) tparams params e doc loc) = do   name' <- transformName name   tdecl' <- traverse transformTypeExp tdecl   t' <- transformStructType t   e' <- transformExp e   tparams' <- traverse transformNames tparams   params' <- traverse transformNames params-  emit $ ValDec $ ValBind entry name' tdecl' (Info t') tparams' params' e' doc loc+  emit $ ValDec $ ValBind entry name' tdecl' (Info (t', retext)) tparams' params' e' doc loc  transformTypeDecl :: TypeDecl -> TransformM TypeDecl transformTypeDecl (TypeDecl dt (Info et)) =
src/Futhark/Internalise/Lambdas.hs view
@@ -57,8 +57,9 @@       outer = (`setOuterSize` I.Var chunk_size)   localScope (scopeOfLParams [chunk_param]) $ do     argtypes <- mapM I.subExpType args-    (orig_chunk_param : params, orig_body, rettype) <-+    (lam_params, orig_body, rettype) <-       internaliseLambda lam $ I.Prim int32 : map outer argtypes+    let orig_chunk_param : params = lam_params     body <- runBodyBinder $ do       letBindNames_ [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size       return orig_body@@ -106,8 +107,8 @@       let sizefun_safe =             all (I.safeExp . I.stmExp) $ I.bodyStms $ I.lambdaBody sizefun'           sizefun_arg_invariant =-            not $ any (`nameIn` freeIn (I.lambdaBody sizefun')) $-            map I.paramName $ lambdaParams sizefun'+            not $ any ((`nameIn` freeIn (I.lambdaBody sizefun')) . I.paramName) $+            lambdaParams sizefun'       if sizefun_safe && sizefun_arg_invariant         then do ses <- bodyBind $ lambdaBody sizefun'                 forM_ (zip inner_shapes ses) $ \(v, se) ->@@ -152,8 +153,9 @@   let chunk_param = I.Param chunk_size $ I.Prim int32       chunktypes = map (`arrayOfRow` I.Var chunk_size) rowts   localScope (scopeOfLParams [chunk_param]) $ do-    (orig_chunk_param : params, orig_body, _) <-+    (lam_params, orig_body, _) <-       internaliseLambda lam $ I.Prim int32 : chunktypes+    let orig_chunk_param : params = lam_params     body <- runBodyBinder $ do       letBindNames_ [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size       return orig_body@@ -188,5 +190,5 @@          lambdaWithIncrement :: I.Body -> InternaliseM I.Body         lambdaWithIncrement lam_body = runBodyBinder $ do-          [eq_class] <- bodyBind lam_body+          eq_class <- head <$> bodyBind lam_body           resultBody <$> mkResult eq_class 0
src/Futhark/Internalise/Monad.hs view
@@ -15,6 +15,7 @@   , lookupFunction   , lookupFunction'   , lookupConst+  , topLevelSizes    , bindFunction   , bindConstant@@ -40,7 +41,6 @@ import Control.Monad.Reader import Control.Monad.Writer import Control.Monad.RWS-import qualified Control.Monad.Fail as Fail import qualified Data.Map.Strict as M  import Futhark.Representation.SOACS@@ -60,7 +60,10 @@  type FunTable = M.Map VName FunInfo -type ConstInfo = (Name, ConstParams, [(SubExp,Type)] -> Maybe [DeclExtType])+type ConstInfo = (Name, ConstParams,+                  [(SubExp,Type)] -> Maybe [DeclExtType],+                  [SubExp] -> InternaliseM (),+                  Names)  type ConstTable = M.Map VName ConstInfo @@ -78,6 +81,7 @@     stateNameSource :: VNameSource   , stateFunTable :: FunTable   , stateConstTable :: ConstTable+  , stateTopLevelSizes :: Names   }  newtype InternaliseResult = InternaliseResult [FunDef SOACS]@@ -100,9 +104,6 @@   getNameSource = gets stateNameSource   putNameSource src = modify $ \s -> s { stateNameSource = src } -instance Fail.MonadFail InternaliseM where-  fail = error . ("InternaliseM: "++)- instance MonadBinder InternaliseM where   type Lore InternaliseM = SOACS   mkExpAttrM pat e = InternaliseM $ mkExpAttrM pat e@@ -130,6 +131,7 @@           InternaliseState { stateNameSource = src                            , stateFunTable = mempty                            , stateConstTable = mempty+                           , stateTopLevelSizes = mempty                            }  substitutingVars :: VarSubstitutions -> InternaliseM a -> InternaliseM a@@ -144,7 +146,7 @@  lookupFunction :: VName -> InternaliseM FunInfo lookupFunction fname = maybe bad return =<< lookupFunction' fname-  where bad = fail $ "Internalise.lookupFunction: Function '" ++ pretty fname ++ "' not found."+  where bad = error $ "Internalise.lookupFunction: Function '" ++ pretty fname ++ "' not found."  lookupConst :: VName -> InternaliseM (Maybe ConstInfo) lookupConst fname = gets $ M.lookup fname . stateConstTable@@ -156,6 +158,13 @@ bindConstant :: VName -> ConstInfo -> InternaliseM () bindConstant cname info =   modify $ \s -> s { stateConstTable = M.insert cname info $ stateConstTable s }++-- | Size names implicitly created by existential top-level constant+-- definitions.  These need special treatment because such a thing+-- does not exist in the core language.+topLevelSizes :: InternaliseM Names+topLevelSizes = gets $ foldMap sizes . stateConstTable+  where sizes (_, _, _, _, x) = x  -- | Execute the given action if 'envDoBoundsChecks' is true, otherwise -- just return an empty list.
src/Futhark/Internalise/Monomorphise.hs view
@@ -32,6 +32,7 @@ import           Control.Monad.Writer hiding (Sum) import           Data.Bitraversable import           Data.Bifunctor+import           Data.List import           Data.Loc import qualified Data.Map.Strict as M import           Data.Maybe@@ -45,6 +46,9 @@ import           Language.Futhark.Semantic (TypeBinding(..)) import           Language.Futhark.TypeChecker.Types +i32 :: TypeBase dim als+i32 = Scalar $ Prim $ Signed Int32+ -- | The monomorphization monad reads 'PolyBinding's and writes 'ValBinding's. -- The 'TypeParam's in a 'ValBinding' can only be shape parameters. --@@ -53,7 +57,7 @@ -- in local functions. data PolyBinding = PolyBinding RecordReplacements                    (VName, [TypeParam], [Pattern],-                     Maybe (TypeExp VName), StructType, Exp, SrcLoc)+                     Maybe (TypeExp VName), StructType, [VName], Exp, SrcLoc)  -- | Mapping from record names to the variable names that contain the -- fields.  This is used because the monomorphiser also expands all@@ -110,9 +114,22 @@ lookupRecordReplacement :: VName -> MonoM (Maybe RecordReplacement) lookupRecordReplacement v = asks $ M.lookup v . envRecordReplacements +-- | Given instantiated type of function, produce size arguments.+type InferSizeArgs = StructType -> [Exp]++-- | The kind of type relative to which we monomorphise.  What is+-- important to us is not the specific dimensions, but merely whether+-- they are known or anonymous (the latter False).+type MonoType = TypeBase Bool ()++monoType :: TypeBase (DimDecl VName) als -> MonoType+monoType = bimap onDim (const ())+  where onDim AnyDim = False+        onDim _      = True+ -- | Mapping from function name and instance list to a new function name in case -- the function has already been instantiated with those concrete types.-type Lifts = [((VName, TypeBase () ()), VName)]+type Lifts = [((VName, MonoType), (VName, InferSizeArgs))]  getLifts :: MonoM Lifts getLifts = MonoM $ lift get@@ -120,31 +137,49 @@ modifyLifts :: (Lifts -> Lifts) -> MonoM () modifyLifts = MonoM . lift . modify -addLifted :: VName -> TypeBase () () -> VName -> MonoM ()-addLifted fname il lifted_fname =-  modifyLifts (((fname, il), lifted_fname) :)+addLifted :: VName -> MonoType -> (VName, InferSizeArgs) -> MonoM ()+addLifted fname il liftf =+  modifyLifts (((fname, il), liftf) :) -lookupLifted :: VName -> TypeBase () () -> MonoM (Maybe VName)+lookupLifted :: VName -> MonoType -> MonoM (Maybe (VName, InferSizeArgs)) lookupLifted fname t = lookup (fname, t) <$> getLifts -transformFName :: VName -> TypeBase () () -> MonoM VName-transformFName fname t-  | baseTag fname <= maxIntrinsicTag = return fname+transformFName :: SrcLoc -> QualName VName -> StructType -> MonoM Exp+transformFName loc fname t+  | baseTag (qualLeaf fname) <= maxIntrinsicTag = return $ var fname   | otherwise = do-      maybe_fname <- lookupLifted fname t-      maybe_funbind <- lookupFun fname+      maybe_fname <- lookupLifted (qualLeaf fname) (monoType t)+      maybe_funbind <- lookupFun $ qualLeaf fname+      t' <- removeTypeVariablesInType t       case (maybe_fname, maybe_funbind) of-        -- The function has already been monomorphized.-        (Just fname', _) -> return fname'+        -- The function has already been monomorphised.+        (Just (fname', infer), _) ->+          return $ applySizeArgs fname' t' $ infer t'         -- An intrinsic function.-        (Nothing, Nothing) -> return fname+        (Nothing, Nothing) -> return $ var fname         -- A polymorphic function.         (Nothing, Just funbind) -> do-          (fname', funbind') <- monomorphizeBinding False funbind t-          tell $ Seq.singleton (fname, funbind')-          addLifted fname t fname'-          return fname'+          (fname', infer, funbind') <- monomorphiseBinding False funbind (monoType t')+          tell $ Seq.singleton (qualLeaf fname, funbind')+          addLifted (qualLeaf fname) (monoType t) (fname', infer)+          return $ applySizeArgs fname' t' $ infer t' +  where var fname' = Var fname' (Info (fromStruct t)) loc++        applySizeArg (i, f) size_arg =+          (i-1,+           Apply f size_arg (Info (Observe, Nothing))+           (Info (foldFunType (replicate i i32) (fromStruct t)), Info [])+           loc)++        applySizeArgs fname' t' size_args =+          snd $ foldl' applySizeArg (length size_args - 1,+                                     Var (qualName fname')+                                     (Info (foldFunType (map (const i32) size_args)+                                            (fromStruct t')))+                                     loc)+          size_args+ -- | This carries out record replacements in the alias information of a type. transformType :: TypeBase dim Aliasing -> MonoM (TypeBase dim Aliasing) transformType t = do@@ -183,8 +218,8 @@           transformField $ RecordFieldExplicit (baseName v)             (Var (qualName v) t' loc) loc -transformExp (ArrayLit es tp loc) =-  ArrayLit <$> mapM transformExp es <*> pure tp <*> pure loc+transformExp (ArrayLit es t loc) =+  ArrayLit <$> mapM transformExp es <*> traverse transformType t <*> pure loc  transformExp (Range e1 me incl tp loc) = do   e1' <- transformExp e1@@ -192,8 +227,8 @@   incl' <- mapM transformExp incl   return $ Range e1' me' incl' tp loc -transformExp (Var (QualName qs fname) (Info t) loc) = do-  maybe_fs <- lookupRecordReplacement fname+transformExp (Var fname (Info t) loc) = do+  maybe_fs <- lookupRecordReplacement $ qualLeaf fname   case maybe_fs of     Just fs -> do       let toField (f, (f_v, f_t)) = do@@ -202,29 +237,31 @@             return $ RecordFieldExplicit f f_v' loc       RecordLit <$> mapM toField (M.toList fs) <*> pure loc     Nothing -> do-      fname' <- transformFName fname (toStructural t)       t' <- transformType t-      return $ Var (QualName qs fname') (Info t') loc+      transformFName loc fname (toStruct t') -transformExp (Ascript e tp t loc) = do-  noticeDims $ unInfo t-  Ascript <$> transformExp e <*> pure tp <*>-    traverse transformType t <*> pure loc+transformExp (Ascript e tp loc) =+  Ascript <$> transformExp e <*> pure tp <*> pure loc -transformExp (LetPat pat e1 e2 (Info t) loc) = do+transformExp (Coerce e tp (Info t, ext) loc) = do+  noticeDims t+  Coerce <$> transformExp e <*> pure tp <*>+    ((,) <$> (Info <$> transformType t) <*> pure ext) <*> pure loc++transformExp (LetPat pat e1 e2 (Info t, retext) loc) = do   (pat', rr) <- transformPattern pat   t' <- transformType t   LetPat pat' <$> transformExp e1 <*>     withRecordReplacements rr (transformExp e2) <*>-    pure (Info t') <*> pure loc+    pure (Info t', retext) <*> pure loc -transformExp (LetFun fname (tparams, params, retdecl, Info ret, body) e loc)+transformExp (LetFun fname (tparams, params, retdecl, Info ret, body) e e_t loc)   | any isTypeParam tparams = do       -- Retrieve the lifted monomorphic function bindings that are produced,       -- filter those that are monomorphic versions of the current let-bound       -- function and insert them at this point, and propagate the rest.       rr <- asks envRecordReplacements-      let funbind = PolyBinding rr (fname, tparams, params, retdecl, ret, body, loc)+      let funbind = PolyBinding rr (fname, tparams, params, retdecl, ret, [], body, loc)       pass $ do         (e', bs) <- listen $ extendEnv fname funbind $ transformExp e         let (bs_local, bs_prop) = Seq.partition ((== fname) . fst) bs@@ -233,20 +270,20 @@   | otherwise = do       body' <- transformExp body       LetFun fname (tparams, params, retdecl, Info ret, body') <$>-        transformExp e <*> pure loc+        transformExp e <*> traverse transformType e_t <*> pure loc -transformExp (If e1 e2 e3 tp loc) = do+transformExp (If e1 e2 e3 (tp, retext) loc) = do   e1' <- transformExp e1   e2' <- transformExp e2   e3' <- transformExp e3   tp' <- traverse transformType tp-  return $ If e1' e2' e3' tp' loc+  return $ If e1' e2' e3' (tp', retext) loc -transformExp (Apply e1 e2 d tp loc) = do+transformExp (Apply e1 e2 d (ret, ext) loc) = do   e1' <- transformExp e1   e2' <- transformExp e2-  tp' <- traverse transformType tp-  return $ Apply e1' e2' d tp' loc+  ret' <- traverse transformType ret+  return $ Apply e1' e2' d (ret', ext) loc  transformExp (Negate e loc) =   Negate <$> transformExp e <*> pure loc@@ -258,17 +295,19 @@ transformExp (OpSection qn t loc) =   transformExp $ Var qn t loc -transformExp (OpSectionLeft (QualName qs fname) (Info t) e-               (Info xtype, Info ytype) (Info rettype) loc) = do-  fname' <- transformFName fname (toStructural t)+transformExp (OpSectionLeft fname (Info t) e+               (Info (xtype, xargext), Info ytype) (Info rettype, Info retext) loc) = do+  fname' <- transformFName loc fname $ toStruct t   e' <- transformExp e-  desugarBinOpSection (QualName qs fname') (Just e') Nothing t xtype ytype rettype loc+  desugarBinOpSection fname' (Just e') Nothing+    t (xtype, xargext) (ytype, Nothing) (rettype, retext) loc -transformExp (OpSectionRight (QualName qs fname) (Info t) e-               (Info xtype, Info ytype) (Info rettype) loc) = do-  fname' <- transformFName fname (toStructural t)+transformExp (OpSectionRight fname (Info t) e+              (Info xtype, Info (ytype, yargext)) (Info rettype) loc) = do+  fname' <- transformFName loc fname $ toStruct t   e' <- transformExp e-  desugarBinOpSection (QualName qs fname') Nothing (Just e') t xtype ytype rettype loc+  desugarBinOpSection fname' Nothing (Just e')+    t (xtype, Nothing) (ytype, yargext) (rettype, []) loc  transformExp (ProjectSection fields (Info t) loc) =   desugarProjectSection fields t loc@@ -276,20 +315,28 @@ transformExp (IndexSection idxs (Info t) loc) =   desugarIndexSection idxs t loc -transformExp (DoLoop pat e1 form e3 loc) = do+transformExp (DoLoop sparams pat e1 form e3 ret loc) = do   e1' <- transformExp e1   form' <- case form of     For ident e2  -> For ident <$> transformExp e2     ForIn pat2 e2 -> ForIn pat2 <$> transformExp e2     While e2      -> While <$> transformExp e2   e3' <- transformExp e3-  return $ DoLoop pat e1' form' e3' loc+  return $ DoLoop sparams pat e1' form' e3' ret loc -transformExp (BinOp (QualName qs fname, oploc) (Info t) (e1, d1) (e2, d2) tp loc) = do-  fname' <- transformFName fname (toStructural t)+transformExp (BinOp (fname, oploc) (Info t) (e1, d1) (e2, d2) tp ext loc) = do+  fname' <- transformFName loc fname $ toStruct t   e1' <- transformExp e1   e2' <- transformExp e2-  return $ BinOp (QualName qs fname', oploc) (Info t) (e1', d1) (e2', d2) tp loc+  return $+    case fname' of+      Var fname'' _ _ ->+        BinOp (fname'', oploc) (Info t) (e1', d1) (e2', d2) tp ext loc+      _ ->+        Apply (Apply fname' e1' (Info (Observe, snd (unInfo d1)))+               (Info (foldFunType [fromStruct $ fst (unInfo d2)] (unInfo tp)),+                Info mempty) loc)+        e2' (Info (Observe, snd (unInfo d2))) (tp, ext) loc  transformExp (Project n e tp loc) = do   maybe_fs <- case e of@@ -329,8 +376,9 @@ transformExp (Constr name all_es t loc) =   Constr name <$> mapM transformExp all_es <*> pure t <*> pure loc -transformExp (Match e cs t loc) =-  Match <$> transformExp e <*> mapM transformCase cs <*> traverse transformType t <*> pure loc+transformExp (Match e cs (t, retext) loc) =+  Match <$> transformExp e <*> mapM transformCase cs <*>+  ((,) <$> traverse transformType t <*> pure retext) <*> pure loc  transformCase :: Case -> MonoM Case transformCase (CasePat p e loc) = do@@ -344,12 +392,17 @@   where trans = mapM transformExp  -- | Transform an operator section into a lambda.-desugarBinOpSection :: QualName VName -> Maybe Exp -> Maybe Exp-                 -> PatternType -> StructType -> StructType -> PatternType -> SrcLoc -> MonoM Exp-desugarBinOpSection qn e_left e_right t xtype ytype rettype loc = do+desugarBinOpSection :: Exp -> Maybe Exp -> Maybe Exp+                    -> PatternType+                    -> (StructType, Maybe VName) -> (StructType, Maybe VName)+                    -> (PatternType, [VName]) -> SrcLoc -> MonoM Exp+desugarBinOpSection op e_left e_right t (xtype, xext) (ytype, yext) (rettype, retext) loc = do   (e1, p1) <- makeVarParam e_left $ fromStruct xtype   (e2, p2) <- makeVarParam e_right $ fromStruct ytype-  let body = BinOp (qn, loc) (Info t) (e1, Info xtype) (e2, Info ytype) (Info rettype) loc+  let apply_left = Apply op e1 (Info (Observe, xext))+                   (Info $ foldFunType [fromStruct ytype] t, Info []) loc+      body = Apply apply_left e2 (Info (Observe, yext))+             (Info rettype, Info retext) loc       rettype' = toStruct rettype   return $ Lambda (p1 ++ p2) body Nothing (Info (mempty, rettype')) loc @@ -376,22 +429,23 @@ desugarIndexSection :: [DimIndex] -> PatternType -> SrcLoc -> MonoM Exp desugarIndexSection idxs (Scalar (Arrow _ _ t1 t2)) loc = do   p <- newVName "index_i"-  let body = Index (Var (qualName p) (Info t1) loc) idxs (Info t2) loc+  let body = Index (Var (qualName p) (Info t1) loc) idxs (Info t2, Info []) loc   return $ Lambda [Id p (Info t1) noLoc] body Nothing (Info (mempty, toStruct t2)) loc desugarIndexSection  _ t _ = error $ "desugarIndexSection: not a function type: " ++ pretty t  noticeDims :: TypeBase (DimDecl VName) as -> MonoM () noticeDims = mapM_ notice . nestedDims-  where notice (NamedDim v) = void $ transformFName (qualLeaf v) $ Scalar $ Prim $ Signed Int32+  where notice (NamedDim v) = void $ transformFName noLoc v i32         notice _            = return ()  -- | Convert a collection of 'ValBind's to a nested sequence of let-bound, -- monomorphic functions with the given expression at the bottom. unfoldLetFuns :: [ValBind] -> Exp -> Exp unfoldLetFuns [] e = e-unfoldLetFuns (ValBind _ fname _ rettype dim_params params body _ loc : rest) e =-  LetFun fname (dim_params, params, Nothing, rettype, body) e' loc+unfoldLetFuns (ValBind _ fname _ (Info (rettype, _)) dim_params params body _ loc : rest) e =+  LetFun fname (dim_params, params, Nothing, Info rettype, body) e' (Info e_t) loc   where e' = unfoldLetFuns rest e+        e_t = typeOf e'  transformPattern :: Pattern -> MonoM (Pattern, RecordReplacements) transformPattern (Id v (Info (Scalar (Record fs))) loc) = do@@ -430,41 +484,96 @@ wildcard t loc =   Wildcard (Info t) loc --- | Monomorphize a polymorphic function at the types given in the instance--- list. Monomorphizes the body of the function as well. Returns the fresh name+type DimInst = M.Map VName (DimDecl VName)++dimMapping :: Monoid a =>+              TypeBase (DimDecl VName) a+           -> TypeBase (DimDecl VName) a+           -> DimInst+dimMapping t1 t2 = execState (matchDims f t1 t2) mempty+  where f (NamedDim d1) d2 = do+          modify $ M.insert (qualLeaf d1) d2+          return $ NamedDim d1+        f d _ = return d++inferSizeArgs :: [TypeParam] -> StructType -> StructType -> [Exp]+inferSizeArgs tparams bind_t t =+  mapMaybe (tparamArg (dimMapping bind_t t)) tparams+  where tparamArg dinst tp =+          case M.lookup (typeParamName tp) dinst of+            Just (NamedDim d) ->+              Just $ Var d (Info i32) noLoc+            Just (ConstDim x) ->+              Just $ Literal (SignedValue $ Int32Value $ fromIntegral x) noLoc+            _ ->+              Nothing++explicitSizes :: StructType -> MonoType -> S.Set VName+explicitSizes t1 t2 =+  execState (matchDims onDims t1 t2) mempty `S.intersection` mustBeExplicit t1+  where onDims d1 d2 = do+          case (d1, d2) of+            (NamedDim v, True) -> modify $ S.insert $ qualLeaf v+            _                  -> return ()+          return d1++-- Monomorphising higher-order functions can result in function types+-- where the same named parameter occurs in multiple spots.  When+-- monomorphising we don't really need those parameter names anymore,+-- and the defunctionaliser can be confused if there are duplicates+-- (it doesn't handle shadowing), so let's just remove all parameter+-- names here.  This is safe because a MonoType does not contain sizes+-- anyway.+noNamedParams :: MonoType -> MonoType+noNamedParams = f+  where f (Array () u t shape) = Array () u (f' t) shape+        f (Scalar t) = Scalar $ f' t+        f' (Arrow () _ t1 t2) =+          Arrow () Unnamed (f t1) (f t2)+        f' (Record fs) =+          Record $ fmap f fs+        f' (Sum cs) =+          Sum $ fmap (map f) cs+        f' t = t++-- | Monomorphise a polymorphic function at the types given in the instance+-- list. Monomorphises the body of the function as well. Returns the fresh name -- of the generated monomorphic function and its 'ValBind' representation.-monomorphizeBinding :: Bool -> PolyBinding -> TypeBase () () -> MonoM (VName, ValBind)-monomorphizeBinding entry (PolyBinding rr (name, tparams, params, retdecl, rettype, body, loc)) t =+monomorphiseBinding :: Bool -> PolyBinding -> MonoType+                    -> MonoM (VName, InferSizeArgs, ValBind)+monomorphiseBinding entry (PolyBinding rr (name, tparams, params, retdecl, rettype, retext, body, loc)) t =   replaceRecordReplacements rr $ do-  t' <- removeTypeVariablesInType t-  let bind_t = foldFunType (map (toStructural . patternType) params) $-               toStructural rettype-  (substs, t_shape_params) <- typeSubstsM loc bind_t t'+  let bind_t = foldFunType (map patternStructType params) rettype+  (substs, t_shape_params) <- typeSubstsM loc (noSizes bind_t) $ noNamedParams t   let substs' = M.map Subst substs       rettype' = substTypesAny (`M.lookup` substs') rettype       substPatternType =         substTypesAny (fmap (fmap fromStruct) . (`M.lookup` substs'))       params' = map (substPattern entry substPatternType) params+      bind_t' = substTypesAny (`M.lookup` substs') bind_t+      (shape_params_explicit, shape_params_implicit) =+        partition ((`S.member` explicitSizes bind_t' t) . typeParamName) $+        shape_params ++ t_shape_params    (params'', rrs) <- unzip <$> mapM transformPattern params'    mapM_ noticeDims $ rettype : map patternStructType params'' -  body' <- updateExpTypes (`M.lookup` M.map (fmap toStructural) substs') body+  body' <- updateExpTypes (`M.lookup` substs') body   body'' <- withRecordReplacements (mconcat rrs) $ transformExp body'-  body''' <- astMap noMoreSumTypes body''-  params''' <- astMap noMoreSumTypes params''-  name' <- if null tparams then return name else newName name-  return (name', toValBinding t_shape_params name' params''' rettype' body''')+  name' <- if null tparams && not entry then return name else newName name -  where shape_params = filter (not . isTypeParam) tparams+  return (name',+          inferSizeArgs shape_params_explicit bind_t',+          if entry+          then toValBinding name'+               (shape_params_explicit++shape_params_implicit) params''+               (rettype', retext) body''+          else toValBinding name' shape_params_implicit+               (map shapeParam shape_params_explicit ++ params'')+               (rettype', retext) body'') -        noMoreSumTypes = ASTMapper { mapOnExp         = pure-                                   , mapOnName        = pure-                                   , mapOnQualName    = pure-                                   , mapOnStructType  = pure-                                   , mapOnPatternType = pure-                                   }+  where shape_params = filter (not . isTypeParam) tparams          updateExpTypes substs = astMap $ mapper substs         mapper substs = ASTMapper { mapOnExp         = astMap $ mapper substs@@ -474,61 +583,57 @@                                   , mapOnPatternType = pure . applySubst substs                                   } -        toValBinding t_shape_params name' params'' rettype' body'' =+        shapeParam tp = Id (typeParamName tp) (Info i32) $ srclocOf tp++        toValBinding name' tparams' params'' rettype' body'' =           ValBind { valBindEntryPoint = Nothing                   , valBindName       = name'                   , valBindRetDecl    = retdecl                   , valBindRetType    = Info rettype'-                  , valBindTypeParams = shape_params ++ t_shape_params+                  , valBindTypeParams = tparams'                   , valBindParams     = params''                   , valBindBody       = body''                   , valBindDoc        = Nothing                   , valBindLocation   = loc                   } --- Careful not to introduce size parameters for non-positive positions--- (i.e. function parameters). typeSubstsM :: MonadFreshNames m =>-               SrcLoc -> TypeBase () () -> TypeBase () ()+               SrcLoc -> TypeBase () () -> MonoType             -> m (M.Map VName StructType, [TypeParam]) typeSubstsM loc orig_t1 orig_t2 =-  let (t1_pts, t1_rt) = unfoldFunType orig_t1-      (t2_pts, t2_rt) = unfoldFunType orig_t2-      m = do zipWithM_ (sub True) t1_pts t2_pts-             sub False t1_rt t2_rt+  let m = sub orig_t1 orig_t2   in runWriterT $ execStateT m mempty -  where sub pos t1@Array{} t2@Array{}+  where sub t1@Array{} t2@Array{}           | Just t1' <- peelArray (arrayRank t1) t1,             Just t2' <- peelArray (arrayRank t1) t2 =-              sub pos t1' t2'-        sub pos (Scalar (TypeVar _ _ v _)) t = addSubst pos v t-        sub pos (Scalar (Record fields1)) (Scalar (Record fields2)) =-          zipWithM_ (sub pos)+              sub t1' t2'+        sub (Scalar (TypeVar _ _ v _)) t = addSubst v t+        sub (Scalar (Record fields1)) (Scalar (Record fields2)) =+          zipWithM_ sub           (map snd $ sortFields fields1) (map snd $ sortFields fields2)-        sub _ (Scalar Prim{}) (Scalar Prim{}) = return ()-        sub _ (Scalar (Arrow _ _ t1a t1b)) (Scalar (Arrow _ _ t2a t2b)) = do-          sub False t1a t2a-          sub False t1b t2b-        sub pos (Scalar (Sum cs1)) (Scalar (Sum cs2)) =+        sub (Scalar Prim{}) (Scalar Prim{}) = return ()+        sub (Scalar (Arrow _ _ t1a t1b)) (Scalar (Arrow _ _ t2a t2b)) = do+          sub t1a t2a+          sub t1b t2b+        sub (Scalar (Sum cs1)) (Scalar (Sum cs2)) =           zipWithM_ typeSubstClause (sortConstrs cs1) (sortConstrs cs2)-          where typeSubstClause (_, ts1) (_, ts2) = zipWithM (sub pos) ts1 ts2-        sub pos t1@(Scalar Sum{}) t2 = sub pos t1 t2-        sub pos t1 t2@(Scalar Sum{}) = sub pos t1 t2+          where typeSubstClause (_, ts1) (_, ts2) = zipWithM sub ts1 ts2+        sub t1@(Scalar Sum{}) t2 = sub t1 t2+        sub t1 t2@(Scalar Sum{}) = sub t1 t2 -        sub _ t1 t2 = error $ unlines ["typeSubstsM: mismatched types:", pretty t1, pretty t2]+        sub t1 t2 = error $ unlines ["typeSubstsM: mismatched types:", pretty t1, pretty t2] -        addSubst pos (TypeName _ v) t = do+        addSubst (TypeName _ v) t = do           exists <- gets $ M.member v           unless exists $ do-            t' <- if pos-                  then bitraverse onDim pure t-                  else pure $ vacuousShapeAnnotations t+            t' <- bitraverse onDim pure t             modify $ M.insert v t' -        onDim () = do d <- lift $ lift $ newVName "d"-                      tell [TypeParamDim d loc]-                      return $ NamedDim $ qualName d+        onDim True = do d <- lift $ lift $ newVName "d"+                        tell [TypeParamDim d loc]+                        return $ NamedDim $ qualName d+        onDim False = return AnyDim  -- | Perform a given substitution on the types in a pattern. substPattern :: Bool -> (PatternType -> PatternType) -> Pattern -> Pattern@@ -545,12 +650,12 @@   PatternConstr n (Info tp) ps loc -> PatternConstr n (Info $ f tp) ps loc  toPolyBinding :: ValBind -> PolyBinding-toPolyBinding (ValBind _ name retdecl (Info rettype) tparams params body _ loc) =-  PolyBinding mempty (name, tparams, params, retdecl, rettype, body, loc)+toPolyBinding (ValBind _ name retdecl (Info (rettype, retext)) tparams params body _ loc) =+  PolyBinding mempty (name, tparams, params, retdecl, rettype, retext, body, loc)  -- | Remove all type variables and type abbreviations from a value binding. removeTypeVariables :: Bool -> ValBind -> MonoM ValBind-removeTypeVariables entry valbind@(ValBind _ _ _ (Info rettype) _ pats body _ _) = do+removeTypeVariables entry valbind@(ValBind _ _ _ (Info (rettype, retext)) _ pats body _ _) = do   subs <- asks $ M.map TypeSub . envTypeBindings   let mapper = ASTMapper {           mapOnExp         = astMap mapper@@ -562,26 +667,26 @@    body' <- astMap mapper body -  return valbind { valBindRetType = Info $ substituteTypes subs rettype+  return valbind { valBindRetType = Info (substituteTypes subs rettype, retext)                  , valBindParams  = map (substPattern entry $ substituteTypes subs) pats                  , valBindBody    = body'                  } -removeTypeVariablesInType :: TypeBase () () -> MonoM (TypeBase () ())+removeTypeVariablesInType :: StructType -> MonoM StructType removeTypeVariablesInType t = do   subs <- asks $ M.map TypeSub . envTypeBindings-  return $ removeShapeAnnotations $ substituteTypes subs $ vacuousShapeAnnotations t+  return $ substituteTypes subs t  transformValBind :: ValBind -> MonoM Env transformValBind valbind = do-  valbind' <- toPolyBinding <$> removeTypeVariables (isJust (valBindEntryPoint valbind)) valbind+  valbind' <- toPolyBinding <$>+              removeTypeVariables (isJust (valBindEntryPoint valbind)) valbind   when (isJust $ valBindEntryPoint valbind) $ do-    t <- removeTypeVariablesInType $ removeShapeAnnotations $ foldFunType+    t <- removeTypeVariablesInType $ foldFunType          (map patternStructType (valBindParams valbind)) $-         unInfo $ valBindRetType valbind-    (name, valbind'') <- monomorphizeBinding True valbind' t+         fst $ unInfo $ valBindRetType valbind+    (name, _, valbind'') <- monomorphiseBinding True valbind' $ monoType t     tell $ Seq.singleton (name, valbind'' { valBindEntryPoint = valBindEntryPoint valbind})-    addLifted (valBindName valbind) t name   return mempty { envPolyBindings = M.singleton (valBindName valbind) valbind' }  transformTypeBind :: TypeBind -> MonoM Env@@ -592,7 +697,7 @@       tbinding = TypeAbbr l tparams tp   return mempty { envTypeBindings = M.singleton name tbinding } --- | Monomorphize a list of top-level declarations. A module-free input program+-- | Monomorphise a list of top-level declarations. A module-free input program -- is expected, so only value declarations and type declaration are accepted. transformDecs :: [Dec] -> MonoM () transformDecs [] = return ()
src/Futhark/Internalise/TypesValues.hs view
@@ -68,14 +68,14 @@                            -> InternaliseM ([[I.TypeBase ExtShape Uniqueness]],                                             ConstParams) internaliseEntryReturnType t = do-  let ts = case E.isTupleRecord t of Just tts -> tts-                                     _        -> [t]+  let ts = case E.isTupleRecord t of Just tts | not $ null tts -> tts+                                     _ -> [t]   runInternaliseTypeM $ mapM internaliseTypeM ts -internaliseType :: E.TypeBase () ()+internaliseType :: E.TypeBase (E.DimDecl VName) ()                 -> InternaliseM [I.TypeBase I.ExtShape Uniqueness] internaliseType =-  fmap fst . runInternaliseTypeM . internaliseTypeM . E.vacuousShapeAnnotations+  fmap fst . runInternaliseTypeM . internaliseTypeM  newId :: InternaliseTypeM Int newId = do (i,cm) <- get@@ -99,14 +99,13 @@              (Nothing, Nothing, Just [v]) -> return $ I.Free v -            (_, Just (fname, _, _), _) -> do+            (_, Just (fname, _, _, _, _), _) -> do               (i,cm) <- get               case find ((==fname) . fst) cm of                 Just (_, known) -> return $ I.Free $ I.Var known                 Nothing -> do new <- liftInternaliseM $ newVName $ baseString name                               put (i, (fname,new):cm)                               return $ I.Free $ I.Var new-             _ -> return $ I.Free $ I.Var name  internaliseTypeM :: E.StructType@@ -119,8 +118,12 @@       return [I.arrayOf et' (Shape dims) $ internaliseUniqueness u | et' <- ets ]     E.Scalar (E.Prim bt) ->       return [I.Prim $ internalisePrimType bt]-    E.Scalar (E.Record ets) ->-      concat <$> mapM (internaliseTypeM . snd) (E.sortFields ets)+    E.Scalar (E.Record ets)+      -- XXX: we map empty records to bools, because otherwise+      -- arrays of unit will lose their sizes.+      | null ets -> return [I.Prim I.Bool]+      | otherwise ->+          concat <$> mapM (internaliseTypeM . snd) (E.sortFields ets)     E.Scalar E.TypeVar{} ->       error "internaliseTypeM: cannot handle type variable."     E.Scalar E.Arrow{} ->@@ -142,8 +145,7 @@                 foldl' f (zip ts [0..], mempty, mempty) c_ts           in (ts ++ new_ts, M.insert c (i, js) mapping)           where f (ts', js, new_ts) t-                  | primType t,-                    Just (_, j) <- find ((==t) . fst) ts' =+                  | Just (_, j) <- find ((==t) . fst) ts' =                       (delete (t, j) ts',                        js ++ [j],                        new_ts)@@ -162,8 +164,8 @@  -- | How many core language values are needed to represent one source -- language value of the given type?-internalisedTypeSize :: E.TypeBase dim () -> InternaliseM Int-internalisedTypeSize = fmap length . internaliseType . E.toStructural+internalisedTypeSize :: E.TypeBase (E.DimDecl VName) () -> InternaliseM Int+internalisedTypeSize = fmap length . internaliseType  -- | Convert an external primitive to an internal primitive. internalisePrimType :: E.PrimType -> I.PrimType
src/Futhark/Optimise/Fusion/TryFusion.hs view
@@ -9,7 +9,6 @@ import Control.Applicative import Control.Monad.State import Control.Monad.Reader-import qualified Control.Monad.Fail as Fail  import Futhark.Representation.SOACS import Futhark.MonadFreshNames@@ -17,7 +16,7 @@ newtype TryFusion a = TryFusion (ReaderT (Scope SOACS)                                  (StateT VNameSource Maybe)                                  a)-  deriving (Functor, Applicative, Alternative, Monad, Fail.MonadFail,+  deriving (Functor, Applicative, Alternative, Monad, MonadFail,             MonadFreshNames,             HasScope SOACS,             LocalScope SOACS)
src/Futhark/Optimise/InliningDeadFun.hs view
@@ -170,7 +170,7 @@        }   where pass prog = do           let cg = buildCallGraph prog-          Prog <$> aggInlining cg (progFunctions prog)+          Prog <$> aggInlining cg (progFuns prog)  -- | @removeDeadFunctions prog@ removes the functions that are unreachable from -- the main function from the program.@@ -182,6 +182,6 @@        }   where pass prog =           let cg        = buildCallGraph prog-              live_funs = filter (isFunInCallGraph cg) (progFunctions prog)+              live_funs = filter (isFunInCallGraph cg) (progFuns prog)           in Prog live_funs         isFunInCallGraph cg fundec = isJust $ M.lookup (funDefName fundec) cg
src/Futhark/Optimise/Simplify/Engine.hs view
@@ -247,7 +247,7 @@   (x, stms) <- m   ops <- asks $ protectHoistedOpS . fst   runBinder $ do-    if any (not . safeExp . stmExp) stms+    if not $ all (safeExp . stmExp) stms       then do cond' <- if side then return cond                        else letSubExp "cond_neg" $ BasicOp $ UnOp Not cond               mapM_ (protectIf ops unsafeOrCostly cond') stms@@ -268,7 +268,7 @@   (x, stms) <- m   ops <- asks $ protectHoistedOpS . fst   runBinder $ do-    if any (not . safeExp . stmExp) stms+    if not $ all (safeExp . stmExp) stms       then do is_nonempty <- checkIfNonEmpty               mapM_ (protectIf ops (not . safeExp) is_nonempty) stms       else addStms stms@@ -315,7 +315,7 @@  emptyOfType :: MonadBinder m => [VName] -> Type -> m (Exp (Lore m)) emptyOfType _ Mem{} =-  fail "emptyOfType: Cannot hoist non-existential memory."+  error "emptyOfType: Cannot hoist non-existential memory." emptyOfType _ (Prim pt) =   return $ BasicOp $ SubExp $ Constant $ blankPrimValue pt emptyOfType ctx_names (Array pt shape _) = do@@ -329,7 +329,7 @@ -- further optimisation.. notWorthHoisting :: Attributes lore => BlockPred lore notWorthHoisting _ _ (Let pat _ e) =-  not (safeExp e) && any (>0) (map arrayRank $ patternTypes pat)+  not (safeExp e) && any ((>0) . arrayRank) (patternTypes pat)  hoistStms :: SimplifiableLore lore =>              RuleBook (Wise lore) -> BlockPred (Wise lore)@@ -514,6 +514,7 @@                     stmsToList $ stms1<>stms2        isNotHoistableBnd _ _ _ (Let _ _ (BasicOp ArrayLit{})) = False+      isNotHoistableBnd _ _ _ (Let _ _ (BasicOp SubExp{})) = False       isNotHoistableBnd nms _ _ stm = not (hasPatName nms stm)        block = branch_blocker `orIf`@@ -804,12 +805,16 @@   simplify (Free se) = Free <$> simplify se   simplify (Ext x)   = return $ Ext x +instance Simplifiable Space where+  simplify (ScalarSpace ds t) = ScalarSpace <$> simplify ds <*> pure t+  simplify s = pure s+ instance Simplifiable shape => Simplifiable (TypeBase shape u) where   simplify (Array et shape u) = do     shape' <- simplify shape     return $ Array et shape' u   simplify (Mem space) =-    pure $ Mem space+    Mem <$> simplify space   simplify (Prim bt) =     return $ Prim bt 
src/Futhark/Optimise/Simplify/Rules.hs view
@@ -286,7 +286,7 @@           (x,x_stms) <- collectStms m           case x of             IndexResult cs arr' slice-              | all (not . (i `nameIn`) . freeIn) x_stms,+              | not $ any ((i `nameIn`) . freeIn) x_stms,                 DimFix (Var j) : slice' <- slice,                 j == i, not $ i `nameIn` freeIn slice -> do                   addStms x_stms
src/Futhark/Optimise/Sink.hs view
@@ -81,10 +81,10 @@ optimiseBranch :: SymbolTable -> Sinking -> Body SinkLore                -> (Body SinkLore, Sunk) optimiseBranch vtable sinking (Body attr stms res) =-  let (stms', stms_sunk) = optimiseStms vtable sinking' stms+  let (stms', stms_sunk) = optimiseStms vtable sinking' stms $ freeIn res   in (Body attr (sunk_stms <> stms') res,       sunk <> stms_sunk)-  where free_in_stms = freeIn stms+  where free_in_stms = freeIn stms <> freeIn res         (sinking_here, sinking') = M.partitionWithKey sunkHere sinking         sunk_stms = stmsFromList $ M.elems sinking_here         sunkHere v stm =@@ -92,15 +92,16 @@           all (`ST.available` vtable) (namesToList (freeIn stm))         sunk = S.fromList $ concatMap (patternNames . stmPattern) sunk_stms -optimiseStms :: SymbolTable -> Sinking -> Stms SinkLore+optimiseStms :: SymbolTable -> Sinking -> Stms SinkLore -> Names              -> (Stms SinkLore, Sunk)-optimiseStms init_vtable init_sinking all_stms =+optimiseStms init_vtable init_sinking all_stms free_in_res =   let (all_stms', sunk) =         optimiseStms' init_vtable init_sinking $ stmsToList all_stms   in (stmsFromList all_stms', sunk)   where-    multiplicities = foldl' (M.unionWith (+)) mempty $-                     map multiplicity $ stmsToList all_stms+    multiplicities = foldl' (M.unionWith (+))+                     (M.fromList (zip (namesToList free_in_res) [1..]))+                     (map multiplicity $ stmsToList all_stms)      optimiseStms' _ _ [] = ([], mempty) @@ -165,13 +166,13 @@ optimiseBody :: SymbolTable -> Sinking -> Body SinkLore              -> (Body SinkLore, Sunk) optimiseBody vtable sinking (Body attr stms res) =-  let (stms', sunk) = optimiseStms vtable sinking stms+  let (stms', sunk) = optimiseStms vtable sinking stms $ freeIn res   in (Body attr stms' res, sunk)  optimiseKernelBody :: SymbolTable -> Sinking -> KernelBody SinkLore                    -> (KernelBody SinkLore, Sunk) optimiseKernelBody vtable sinking (KernelBody attr stms res) =-  let (stms', sunk) = optimiseStms vtable sinking stms+  let (stms', sunk) = optimiseStms vtable sinking stms $ freeIn res   in (KernelBody attr stms' res, sunk)  optimiseFunDef :: MonadFreshNames m => FunDef Kernels -> m (FunDef Kernels)
src/Futhark/Optimise/TileLoops.hs view
@@ -22,7 +22,7 @@  tileLoops :: Pass Kernels Kernels tileLoops = Pass "tile loops" "Tile stream loops inside kernels" $-            fmap Prog . mapM optimiseFunDef . progFunctions+            fmap Prog . mapM optimiseFunDef . progFuns  optimiseFunDef :: MonadFreshNames m => FunDef Kernels -> m (FunDef Kernels) optimiseFunDef fundec = do@@ -86,8 +86,8 @@       -- 1D tiling of redomap.       | (gtid, kdim) : top_space_rev <- reverse $ unSegSpace initial_space,         Just (w, arrs, form) <- tileable stm_to_tile,-        all (not . nameIn gtid .-             flip (M.findWithDefault mempty) variance) arrs,+        not $ any (nameIn gtid .+                   flip (M.findWithDefault mempty) variance) arrs,         not $ gtid `nameIn` branch_variant,         (prestms', poststms') <-           preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms),@@ -273,7 +273,7 @@          mergeinit' <-           fmap (map Var) $ certifying (stmAuxCerts aux) $-          tilingSegMap tiling "tiled_loopinit" (scalarLevel tiling) ResultNoSimplify $+          tilingSegMap tiling "tiled_loopinit" (scalarLevel tiling) ResultPrivate $           \in_bounds slice ->             fmap (map Var) $ protectOutOfBounds "loopinit" in_bounds merge_ts $ do             addPrivStms slice inloop_privstms@@ -304,7 +304,7 @@ doPrelude :: Tiling -> Stms Kernels -> [VName] -> Binder Kernels [VName] doPrelude tiling prestms prestms_live =   -- Create a SegMap that takes care of the prelude for every thread.-  tilingSegMap tiling "prelude" (scalarLevel tiling) ResultMaySimplify $+  tilingSegMap tiling "prelude" (scalarLevel tiling) ResultPrivate $   \in_bounds _slice -> do     ts <- mapM lookupType prestms_live     fmap (map Var) $ letTupExp "pre" =<<@@ -399,7 +399,7 @@  scalarLevel :: Tiling -> SegLevel scalarLevel tiling =-  SegThreadScalar (segNumGroups lvl) (segGroupSize lvl) SegNoVirt+  SegThread (segNumGroups lvl) (segGroupSize lvl) SegNoVirt   where lvl = tilingLevel tiling  protectOutOfBounds :: String -> PrimExp VName -> [Type] -> Binder Kernels [SubExp]@@ -412,15 +412,19 @@                 -> Stms Kernels -> Result -> [Type]                 -> Binder Kernels [VName] postludeGeneric tiling privstms pat accs' poststms poststms_res res_ts =-  tilingSegMap tiling "thread_res" (scalarLevel tiling) ResultMaySimplify $ \in_bounds slice -> do+  tilingSegMap tiling "thread_res" (scalarLevel tiling) ResultPrivate $ \in_bounds slice -> do     -- Read our per-thread result from the tiled loop.     forM_ (zip (patternNames pat) accs') $ \(us, everyone) ->       letBindNames_ [us] $ BasicOp $ Index everyone slice      if poststms == mempty-      then return poststms_res-      else fmap (map Var) $ protectOutOfBounds "postlude" in_bounds res_ts $ do+      then do -- The privstms may still be necessary for the result.       addPrivStms slice privstms+      return poststms_res++      else+      fmap (map Var) $ protectOutOfBounds "postlude" in_bounds res_ts $ do+      addPrivStms slice privstms       addStms poststms       return poststms_res @@ -453,7 +457,7 @@        -- We don't use a Replicate here, because we want to enforce a       -- scalar memory space.-      mergeinits <- tilingSegMap tiling "mergeinit" (scalarLevel tiling) ResultNoSimplify $ \in_bounds slice ->+      mergeinits <- tilingSegMap tiling "mergeinit" (scalarLevel tiling) ResultPrivate $ \in_bounds slice ->         -- Constant neutral elements (a common case) do not need protection from OOB.         if freeIn red_nes == mempty           then return red_nes@@ -588,7 +592,7 @@    let tile = map fst tiles_and_perm -  segMap1D "acc" (SegThreadScalar num_groups group_size SegNoVirt) ResultMaySimplify $ \ltid -> do+  segMap1D "acc" (SegThread num_groups group_size SegNoVirt) ResultPrivate $ \ltid -> do      reconstructGtids1D group_size gtid gid ltid     addPrivStms [DimFix $ Var ltid] privstms@@ -794,8 +798,8 @@   -- Might be truncated in case of a partial tile.   actual_tile_size <- arraysSize 0 <$> mapM (lookupType . fst) tiles_and_perms -  segMap2D "acc" (SegThreadScalar num_groups group_size SegNoVirt)-    ResultMaySimplify (tile_size, tile_size) $ \(ltid_x, ltid_y) -> do+  segMap2D "acc" (SegThread num_groups group_size SegNoVirt)+    ResultPrivate (tile_size, tile_size) $ \(ltid_x, ltid_y) -> do     reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)      addPrivStms [DimFix $ Var ltid_x, DimFix $ Var ltid_y] privstms
src/Futhark/Pass.hs view
@@ -80,7 +80,7 @@                               -> Prog fromlore -> PassM (Prog tolore) intraproceduralTransformation ft prog =   either onError onSuccess <=< modifyNameSource $ \src ->-  case partitionEithers $ parMap rpar (onFunction src) (progFunctions prog) of+  case partitionEithers $ parMap rpar (onFunction src) (progFuns prog) of     ([], rs) -> let (funs, logs, srcs) = unzip3 rs                 in (Right (Prog funs, mconcat logs), mconcat srcs)     ((err,log,src'):_, _) -> (Left (err, log), src')
src/Futhark/Pass/ExpandAllocations.hs view
@@ -36,7 +36,7 @@ expandAllocations :: Pass ExplicitMemory ExplicitMemory expandAllocations =   Pass "expand allocations" "Expand allocations" $-  fmap Prog . mapM transformFunDef . progFunctions+  fmap Prog . mapM transformFunDef . progFuns   -- Cannot use intraproceduralTransformation because it might create   -- duplicate size keys (which are not fixed by renamer, and size   -- keys must currently be globally unique).@@ -202,15 +202,18 @@ extractStmAllocations :: Names -> Stm ExplicitMemory                       -> Writer Extraction (Maybe (Stm ExplicitMemory)) extractStmAllocations bound_outside (Let (Pattern [] [patElem]) _ (Op (Alloc size space)))-  | space `notElem`-    [Space "private", Space "local"] ++-    map Space (M.keys allScalarMemory),+  | expandable space,     visibleOutside size = do       tell $ M.singleton (patElemName patElem) (size, space)       return Nothing          where visibleOutside (Var v) = v `nameIn` bound_outside               visibleOutside Constant{} = True++              expandable (Space "private") = False+              expandable (Space "local") = False+              expandable ScalarSpace{} = False+              expandable _ = True  extractStmAllocations bound_outside stm = do   e <- mapExpM expMapper $ stmExp stm
src/Futhark/Pass/ExplicitAllocations.hs view
@@ -18,8 +18,8 @@ import Control.Monad.RWS.Strict import qualified Data.Map.Strict as M import qualified Data.Set as S-import qualified Control.Monad.Fail as Fail import Data.Maybe+import Data.List (zip4, partition, sort)  import Futhark.Representation.Kernels import Futhark.Optimise.Simplify.Lore@@ -141,9 +141,6 @@              LocalScope tolore,              MonadReader (AllocEnv fromlore tolore)) -instance Fail.MonadFail (AllocM fromlore tolore) where-  fail = error . ("AllocM.fail: "++)- instance (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>          MonadBinder (AllocM fromlore tolore) where   type Lore (AllocM fromlore tolore) = tolore@@ -253,7 +250,7 @@                        [AllocStm]) allocsForPattern sizeidents validents rts hints = do   let sizes' = [ PatElem size $ MemPrim int32 | size <- map identName sizeidents ]-  (vals, (mems, postbnds)) <-+  (vals, (exts, mems, postbnds)) <-     runWriterT $ forM (zip3 validents rts hints) $ \(ident, rt, hint) -> do       let shape = arrayShape $ identType ident       case rt of@@ -265,32 +262,74 @@           return $ PatElem (identName ident) $           MemMem space -        MemArray bt _ u (Just (ReturnsInBlock mem ixfun)) ->-          PatElem (identName ident) . MemArray bt shape u .-          ArrayIn mem <$> instantiateIxFun ixfun+        MemArray bt _ u (Just (ReturnsInBlock mem extixfun)) -> do+          (patels, ixfn) <- instantiateExtIxFun ident extixfun+          tell (patels, [], []) +          return $ PatElem (identName ident) $+            MemArray bt shape u $+            ArrayIn mem ixfn+         MemArray _ extshape _ Nothing           | Just _ <- knownShape extshape -> do             summary <- lift $ summaryForBindage (identType ident) hint             return $ PatElem (identName ident) summary -        MemArray bt _ u ret -> do-          space <- case ret of-                     Just (ReturnsNewBlock mem_space _ _) -> return mem_space-                     _                                    -> lift askDefaultSpace-          (mem,(ident',ixfun)) <- lift $ memForBindee ident space-          tell ([PatElem (identName mem) $ MemMem space],-                [])-          return $ PatElem (identName ident') $ MemArray bt shape u $-            ArrayIn (identName mem) ixfun+        MemArray bt _ u (Just (ReturnsNewBlock space _ extixfn)) -> do+          -- treat existential index function first+          (patels, ixfn) <- instantiateExtIxFun ident extixfn+          tell (patels, [], []) -  return (sizes' <> mems,+          memid <- lift $ mkMemIdent ident space+          tell ([], [PatElem (identName memid) $ MemMem space], [])+          return $ PatElem (identName ident) $ MemArray bt shape u $+            ArrayIn (identName memid) ixfn++        _ -> error "Impossible case reached in allocsForPattern!"++  return (sizes' <> exts <> mems,           vals,           postbnds)   where knownShape = mapM known . shapeDims         known (Free v) = Just v         known Ext{} = Nothing +        mkMemIdent :: (MonadFreshNames m) => Ident -> Space -> m Ident+        mkMemIdent ident space = do+          let memname = baseString (identName ident) <> "_mem"+          newIdent memname $ Mem space++        instantiateExtIxFun :: MonadFreshNames m =>+                               Ident -> ExtIxFun ->+                               m ([PatElemT (MemInfo d u ret)], IxFun)+        instantiateExtIxFun idd ext_ixfn = do+          let isAndPtps = S.toList $+                          foldMap onlyExts $+                          foldMap leafExpTypes ext_ixfn++          -- Find the existentials that reuse the sizeidents, and+          -- those that need new pattern elements.  Assumes that the+          -- Exts form a contiguous interval of integers.+          let (size_exts, new_exts) =+                span ((<length sizeidents) . fst) $ sort isAndPtps+          (new_substs, patels) <-+            fmap unzip $ forM new_exts $ \(i, t) -> do+            v <- newVName $ baseString (identName idd) <> "_ixfn"+            return ((Ext i, LeafExp (Free v) t),+                    PatElem v $ MemPrim t)+          let size_substs = zipWith (\(i, t) ident ->+                                    (Ext i, LeafExp (Free (identName ident)) t))+                            size_exts sizeidents+              substs = M.fromList $ new_substs <> size_substs+          ixfn <- instantiateIxFun $ IxFun.substituteInIxFun substs ext_ixfn++          return (patels, ixfn)++onlyExts :: (Ext a, PrimType) -> S.Set (Int, PrimType)+onlyExts (Free _, _) = S.empty+onlyExts (Ext i, t) = S.singleton (i, t)++ instantiateIxFun :: Monad m => ExtIxFun -> m IxFun instantiateIxFun = traverse $ traverse inst   where inst Ext{} = error "instantiateIxFun: not yet"@@ -314,16 +353,12 @@   m <- allocateMemory "mem" bytes space   return $ MemArray bt (arrayShape t) NoUniqueness $ ArrayIn m ixfun -memForBindee :: (MonadFreshNames m) =>-                Ident -> Space-             -> m (Ident,-                   (Ident, IxFun))-memForBindee ident space = do-  mem <- newIdent memname (Mem space)-  return (mem,-          (ident, IxFun.iota $ map (primExpFromSubExp int32) $ arrayDims t))-  where  memname = baseString (identName ident) <> "_mem"-         t       = identType ident+lookupMemSpace :: (HasScope lore m, Monad m) => VName -> m Space+lookupMemSpace v = do+  t <- lookupType v+  case t of+    Mem space -> return space+    _ -> error $ "lookupMemSpace: " ++ pretty v ++ " is not a memory block."  directIndexFunction :: PrimType -> Shape -> u -> VName -> Type -> MemBound u directIndexFunction bt shape u mem t =@@ -383,7 +418,7 @@   where allocInMergeParam (mergeparam, Var v)           | Array bt shape u <- paramDeclType mergeparam = do               (mem, ixfun) <- lift $ lookupArraySummary v-              Mem space <- lift $ lookupType mem+              space <- lift $ lookupMemSpace mem               reuse <- asks aggressiveReuse               if space /= Space "local" &&                  reuse &&@@ -409,7 +444,7 @@                  Type -> VName -> IxFun -> SubExp               -> AllocM fromlore tolore SubExp ensureArrayIn _ _ _ (Constant v) =-  fail $ "ensureArrayIn: " ++ pretty v ++ " cannot be an array."+  error $ "ensureArrayIn: " ++ pretty v ++ " cannot be an array." ensureArrayIn t mem ixfun (Var v) = do   (src_mem, src_ixfun) <- lookupArraySummary v   if src_mem == mem && src_ixfun == ixfun@@ -426,7 +461,7 @@                      Maybe Space -> VName -> AllocM fromlore tolore (VName, SubExp) ensureDirectArray space_ok v = do   (mem, ixfun) <- lookupArraySummary v-  Mem mem_space <- lookupType mem+  mem_space <- lookupMemSpace mem   default_space <- askDefaultSpace   if IxFun.isDirect ixfun && maybe True (==mem_space) space_ok     then return (mem, Var v)@@ -510,7 +545,7 @@ handleHostOp (SizeOp op) =   return $ Inner $ SizeOp op handleHostOp (OtherOp op) =-  fail $ "Cannot allocate memory in SOAC: " ++ pretty op+  error $ "Cannot allocate memory in SOAC: " ++ pretty op handleHostOp (SegOp op) =   Inner . SegOp <$> handleSegOp op @@ -528,7 +563,6 @@                                        , aggressiveReuse = True                                        }   where space = case lvl of SegThread{} -> DefaultSpace-                            SegThreadScalar{} -> DefaultSpace                             SegGroup{} -> Space "local"  bodyReturnMemCtx :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>@@ -554,14 +588,17 @@     return $ Body () (bnds'<>allocs) res''   where num_vals = length space_oks         space_oks' = replicate (length res - num_vals) Nothing ++ space_oks-        ensureDirect _ se@Constant{} = return se-        ensureDirect space_ok (Var v) = do-          bt <- primType <$> lookupType v-          if bt-            then return $ Var v-            else do (_, v') <- ensureDirectArray space_ok v-                    return v' +ensureDirect :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+                Maybe Space -> SubExp -> AllocM fromlore tolore SubExp+ensureDirect _ se@Constant{} = return se+ensureDirect space_ok (Var v) = do+  bt <- primType <$> lookupType v+  if bt+    then return $ Var v+    else do (_, v') <- ensureDirectArray space_ok v+            return v'+ allocInStms :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>                Stms fromlore -> (Stms tolore -> AllocM fromlore tolore a)             -> AllocM fromlore tolore a@@ -611,25 +648,67 @@ allocInExp (Apply fname args rettype loc) = do   args' <- funcallArgs args   return $ Apply fname args' (memoryInRetType rettype) loc-allocInExp (If cond tbranch fbranch (IfAttr rets ifsort)) = do-  tbranch' <- allocInFunBody (map (const Nothing) rets) tbranch-  space_oks <- mkSpaceOks (length rets) tbranch'-  fbranch' <- allocInFunBody space_oks fbranch-  let rets' = createBodyReturns rets space_oks-      res_then = bodyResult tbranch'-      res_else = bodyResult fbranch'-      size_ext = length res_then - length rets'-      (ind_ses0, r_then_else) =-        foldl (\(acc_ise,acc_ext) (r_then, r_else, i) ->-                if r_then == r_else then ((i,r_then):acc_ise, acc_ext)-                else (acc_ise, (r_then, r_else):acc_ext)-              ) ([],[]) $ reverse $ zip3 res_then res_else [0..size_ext-1]-      (r_then_ext, r_else_ext) = unzip r_then_else-      ind_ses = zipWith (\(i,se) k -> (i-k,se)) ind_ses0 [0..length ind_ses0 - 1]-      rets'' = foldl (\acc (i,se) -> fixExt i se acc) rets' ind_ses-      tbranch'' = tbranch' { bodyResult = r_then_ext ++ drop size_ext res_then }-      fbranch'' = fbranch' { bodyResult = r_else_ext ++ drop size_ext res_else }-  return $ If cond tbranch'' fbranch'' $ IfAttr rets'' ifsort+allocInExp (If cond tbranch0 fbranch0 (IfAttr rets ifsort)) = do+  let num_rets = length rets+  -- switch to the explicit-mem rep, but do nothing about results+  (tbranch, tm_ixfs) <- allocInIfBody num_rets tbranch0+  (fbranch, fm_ixfs) <- allocInIfBody num_rets fbranch0+  tspaces <- mkSpaceOks num_rets tbranch+  fspaces <- mkSpaceOks num_rets fbranch+  -- try to generalize (antiunify) the index functions of the then and else bodies+  let sp_substs = zipWith generalize (zip tspaces tm_ixfs) (zip fspaces fm_ixfs)+      (spaces, subs) = unzip sp_substs+      tsubs = map (selectSub fst) subs+      fsubs = map (selectSub snd) subs+  (tbranch', trets) <- addResCtxInIfBody rets tbranch spaces tsubs+  (fbranch', frets) <- addResCtxInIfBody rets fbranch spaces fsubs+  if frets /= trets then error "In allocInExp, IF case: antiunification of then/else produce different ExtInFn!"+    else do -- above is a sanity check; implementation continues on else branch+    let res_then = bodyResult tbranch'+        res_else = bodyResult fbranch'+        size_ext = length res_then - length trets+        (ind_ses0, r_then_else) =+            partition (\(r_then, r_else, _) -> r_then == r_else) $+            zip3 res_then res_else [0 .. size_ext - 1]+        (r_then_ext, r_else_ext, _) = unzip3 r_then_else+        ind_ses = zipWith (\(se, _, i) k -> (i-k, se)) ind_ses0+                  [0 .. length ind_ses0 - 1]+        rets'' = foldl (\acc (i, se) -> fixExt i se acc) trets ind_ses+        tbranch'' = tbranch' { bodyResult = r_then_ext ++ drop size_ext res_then }+        fbranch'' = fbranch' { bodyResult = r_else_ext ++ drop size_ext res_else }+        res_if_expr = If cond tbranch'' fbranch'' $ IfAttr rets'' ifsort+    return res_if_expr+      where generalize :: (Maybe Space, Maybe MemBind) -> (Maybe Space, Maybe MemBind)+                       -> (Maybe Space, Maybe (ExtIxFun, [(PrimExp VName, PrimExp VName)]))+            generalize (Just sp1, Just (ArrayIn _ ixf1)) (Just sp2, Just (ArrayIn _ ixf2)) =+              if sp1 /= sp2 then (Just sp1, Nothing)+              else case IxFun.leastGeneralGeneralization ixf1 ixf2 of+                Just (ixf, m) -> (Just sp1, Just (ixf, m))+                Nothing -> (Just sp1, Nothing)+            generalize (mbsp1, _) _ = (mbsp1, Nothing)++            selectSub :: ((a, a) -> a) -> Maybe (ExtIxFun, [(a, a)]) ->+                         Maybe (ExtIxFun, [a])+            selectSub f (Just (ixfn, m)) = Just (ixfn, map f m)+            selectSub _ Nothing = Nothing++            -- | Just introduces the new representation (index functions); but+            -- does not unify (e.g., does not ensures direct); implementation+            -- extends `allocInBodyNoDirect`, but also return `MemBind`+            allocInIfBody :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+                             Int -> Body fromlore -> AllocM fromlore tolore (Body tolore, [Maybe MemBind])+            allocInIfBody num_vals (Body _ bnds res) =+              allocInStms bnds $ \bnds' -> do+                let (_, val_res) = splitFromEnd num_vals res+                mem_ixfs <- mapM bodyReturnMIxf val_res+                return (Body () bnds' res, mem_ixfs)+                  where+                    bodyReturnMIxf Constant{} = return Nothing+                    bodyReturnMIxf (Var v) = do+                      info <- lookupMemInfo v+                      case info of+                        MemArray _ptp _shp _u mem_ixf -> return $ Just mem_ixf+                        _ -> return Nothing allocInExp e = mapExpM alloc e   where alloc =           identityMapper { mapOnBody = error "Unhandled Body in ExplicitAllocations"@@ -641,6 +720,79 @@                                                handle op                          } +addResCtxInIfBody :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+                     [ExtType] -> Body tolore -> [Maybe Space] ->+                     [Maybe (ExtIxFun, [PrimExp VName])] ->+                     AllocM fromlore tolore (Body tolore, [BodyReturns])+addResCtxInIfBody ifrets (Body _ bnds res) spaces substs = do+  let num_vals = length ifrets+      (ctx_res, val_res) = splitFromEnd num_vals res+  ((res', bodyrets'), all_body_stms) <- collectStms $ do+    mapM_ addStm bnds+    (val_res', ext_ses_res, mem_ctx_res, bodyrets, total_existentials) <-+      foldM helper ([], [], [], [], length ctx_res) (zip4 ifrets val_res substs spaces)+    return (ctx_res <> ext_ses_res <> mem_ctx_res <> val_res',+             -- We need to adjust the ReturnsNewBlock existentials, because they+             -- should always be numbered _after_ all other existentials in the+             -- return values.+            reverse $ fst $ foldl adjustNewBlockExistential ([], total_existentials) bodyrets)+  body' <- mkBodyM all_body_stms res'+  return (body', bodyrets')+    where+      helper (res_acc, ext_acc, ctx_acc, br_acc, k) (ifr, r, mbixfsub, sp) =+        case mbixfsub of+          Nothing -> do+            -- does NOT generalize/antiunify; ensure direct+            r' <- ensureDirect sp r+            mem_ctx_r <- bodyReturnMemCtx r'+            let body_ret = inspect ifr sp+            return (res_acc ++ [r'],+                    ext_acc,+                    ctx_acc ++ mem_ctx_r,+                    br_acc ++ [body_ret],+                    k)+          Just (ixfn, m) -> do -- generalizes+            let i = length m+            ext_ses <- mapM (primExpToSubExp "ixfn_exist"+                             (return . BasicOp . SubExp . Var))+                       m+            mem_ctx_r <- bodyReturnMemCtx r+            let sp' = fromMaybe DefaultSpace sp+                ixfn' = fmap (adjustExtPE k) ixfn+                exttp = case ifr of+                          Array pt shp' u ->+                            MemArray pt shp' u $+                            ReturnsNewBlock sp' 0 ixfn'+                          _ -> error "Impossible case reached in addResCtxInIfBody"+            return (res_acc ++ [r],+                    ext_acc ++ ext_ses,+                    ctx_acc ++ mem_ctx_r,+                    br_acc ++ [exttp],+                    k + i)++      adjustNewBlockExistential :: ([BodyReturns], Int) -> BodyReturns -> ([BodyReturns], Int)+      adjustNewBlockExistential (acc, k) (MemArray pt shp u (ReturnsNewBlock space _ ixfun)) =+        (MemArray pt shp u (ReturnsNewBlock space k ixfun) : acc, k + 1)+      adjustNewBlockExistential (acc, k) x = (x : acc, k)++      inspect (Array pt shape u) space =+        let space' = fromMaybe DefaultSpace space+            bodyret = MemArray pt shape u $ ReturnsNewBlock space' 0 $+              IxFun.iota $ map convert $ shapeDims shape+        in bodyret+      inspect (Prim pt) _ = MemPrim pt+      inspect (Mem space) _ = MemMem space++      convert (Ext i) = LeafExp (Ext i) int32+      convert (Free v) = Free <$> primExpFromSubExp int32 v++      adjustExtV :: Int -> Ext VName -> Ext VName+      adjustExtV _ (Free v) = Free v+      adjustExtV k (Ext i) = Ext (k + i)++      adjustExtPE :: Int -> PrimExp (Ext VName) -> PrimExp (Ext VName)+      adjustExtPE k = fmap (adjustExtV k)+ mkSpaceOks :: (ExplicitMemorish tolore, LocalScope tolore m) =>               Int -> Body tolore -> m [Maybe Space] mkSpaceOks num_vals (Body _ stms res) =@@ -655,22 +807,6 @@                          _ -> return Nothing         mkSpaceOK _ = return Nothing -createBodyReturns :: [ExtType] -> [Maybe Space] -> [BodyReturns]-createBodyReturns ts spaces =-  evalState (zipWithM inspect ts spaces) $ S.size $ shapeContext ts-  where inspect (Array pt shape u) space = do-          i <- get <* modify (+1)-          let space' = fromMaybe DefaultSpace space-          return $ MemArray pt shape u $ ReturnsNewBlock space' i $-            IxFun.iota $ map convert $ shapeDims shape-        inspect (Prim pt) _ =-          return $ MemPrim pt-        inspect (Mem space) _ =-          return $ MemMem space--        convert (Ext i) = LeafExp (Ext i) int32-        convert (Free v) = Free <$> primExpFromSubExp int32 v- allocInLoopForm :: (Allocable fromlore tolore,                     Allocator tolore (AllocM fromlore tolore)) =>                    LoopForm fromlore -> AllocM fromlore tolore (LoopForm tolore)@@ -680,10 +816,11 @@   where allocInLoopVar (p,a) = do           (mem, ixfun) <- lookupArraySummary a           case paramType p of-            Array bt shape u ->+            Array bt shape u -> do+              dims <- map (primExpFromSubExp int32) . arrayDims <$> lookupType a               let ixfun' = IxFun.slice ixfun $-                           fullSliceNum (IxFun.shape ixfun) [DimFix $ LeafExp i int32]-              in return (p { paramAttr = MemArray bt shape u $ ArrayIn mem ixfun' }, a)+                           fullSliceNum dims [DimFix $ LeafExp i int32]+              return (p { paramAttr = MemArray bt shape u $ ArrayIn mem ixfun' }, a)             Prim bt ->               return (p { paramAttr = MemPrim bt }, a)             Mem space ->@@ -720,12 +857,12 @@               let t = paramType x `arrayOfRow` twice_num_threads               mem <- allocForArray t DefaultSpace               -- XXX: this iota ixfun is a bit inefficient; leading to uncoalesced access.-              let ixfun_base = IxFun.iota $-                               map (primExpFromSubExp int32) (arrayDims t)+              let base_dims = map (primExpFromSubExp int32) (arrayDims t)+                  ixfun_base = IxFun.iota base_dims                   ixfun_x = IxFun.slice ixfun_base $-                            fullSliceNum (IxFun.shape ixfun_base) [DimFix my_id]+                            fullSliceNum base_dims [DimFix my_id]                   ixfun_y = IxFun.slice ixfun_base $-                            fullSliceNum (IxFun.shape ixfun_base) [DimFix other_id]+                            fullSliceNum base_dims [DimFix other_id]               return (x { paramAttr = MemArray bt shape u $ ArrayIn mem ixfun_x },                       y { paramAttr = MemArray bt shape u $ ArrayIn mem ixfun_y })             Prim bt ->@@ -748,7 +885,6 @@ allocInKernelBody lvl (KernelBody () stms res) =   local f $ allocInStms stms $ \stms' -> return $ KernelBody () stms' res   where f = case lvl of SegThread{} -> inThread-                        SegThreadScalar{} -> inThread                         SegGroup{} -> inGroup         inThread env = env { envExpHints = inThreadExpHints }         inGroup env = env { envExpHints = inGroupExpHints }@@ -910,15 +1046,18 @@       ixfun_rearranged = IxFun.permute ixfun_base perm_inv   in ixfun_rearranged -inGroupExpHints :: Allocator ExplicitMemory m => Exp ExplicitMemory -> m [ExpHint]-inGroupExpHints (Op (Inner (SegOp (SegMap SegThreadScalar{} space ts _)))) = return $ do-  t <- ts-  case t of-    Prim pt ->-      return $ Hint (IxFun.iota $ map (primExpFromSubExp int32) $-                     segSpaceDims space ++ arrayDims t) $ Space $ scalarMemory pt-    _ ->-      return NoHint+inGroupExpHints :: Exp ExplicitMemory -> AllocM Kernels ExplicitMemory [ExpHint]+inGroupExpHints (Op (Inner (SegOp (SegMap _ space ts body))))+  | any private $ kernelBodyResult body = return $ do+      (t, r) <- zip ts $ kernelBodyResult body+      return $+        if private r+        then let dims = map (primExpFromSubExp int32) $+                            segSpaceDims space ++ arrayDims t+             in Hint (IxFun.iota dims) $ ScalarSpace (arrayDims t) $ elemType t+        else NoHint+  where private (Returns ResultPrivate _) = True+        private _                         = False inGroupExpHints e = return $ replicate (expExtTypeSize e) NoHint  inThreadExpHints :: Allocator ExplicitMemory m => Exp ExplicitMemory -> m [ExpHint]
src/Futhark/Pass/ExtractKernels.hs view
@@ -189,7 +189,7 @@ extractKernels =   Pass { passName = "extract kernels"        , passDescription = "Perform kernel extraction"-       , passFunction = fmap Prog . mapM transformFunDef . progFunctions+       , passFunction = fmap Prog . mapM transformFunDef . progFuns        }  -- In order to generate more stable threshold names, we keep track of@@ -434,7 +434,7 @@    where     paralleliseOuter path'-      | any (not . primType) $ lambdaReturnType red_fun = do+      | not $ all primType $ lambdaReturnType red_fun = do           -- Split into a chunked map and a reduction, with the latter           -- further transformed.           let fold_fun' = soacsLambdaToKernels fold_fun
src/Futhark/Pass/ExtractKernels/DistributeNests.hs view
@@ -305,7 +305,7 @@ maybeDistributeStm stm@(Let pat _ (If cond tbranch fbranch ret)) acc   | null (patternContextElements pat),     bodyContainsParallelism tbranch || bodyContainsParallelism fbranch ||-    any (not . primType) (ifReturns ret) =+    not (all primType (ifReturns ret)) =     distributeSingleStm acc stm >>= \case       Just (kernels, res, nest, acc')         | not $@@ -466,6 +466,21 @@     let rots' = map (const $ intConst Int32 0) (kernelNestWidths nest) ++ rots     return $ oneStm $ Let outerpat aux $ BasicOp $ Rotate rots' arr +maybeDistributeStm stm@(Let pat aux (BasicOp (Update arr slice (Var v)))) acc+  | not $ null $ sliceDims slice =+    distributeSingleStm acc stm >>= \case+    Just (kernels, res, nest, acc')+      | res == map Var (patternNames $ stmPattern stm),+        Just (perm, pat_unused) <- permutationAndMissing pat res -> do+          addKernels kernels+          localScope (typeEnvFromDistAcc acc') $ do+            nest' <- expandKernelNest pat_unused nest+            addKernel =<<+              segmentedUpdateKernel nest' perm (stmAuxCerts aux) arr slice v+            return acc'++    _ -> addStmToKernel stm acc+ -- XXX?  This rule is present to avoid the case where an in-place -- update is distributed as its own kernel, as this would mean thread -- then writes the entire array that it updated.  This is problematic@@ -663,6 +678,50 @@           [ (map Var (init gtids)++[i], v) | (i,v) <- is_vs ]           where (gtids,ws) = unzip ispace +segmentedUpdateKernel :: MonadFreshNames m =>+                         KernelNest+                      -> [Int]+                      -> Certificates+                      -> VName+                      -> Slice SubExp+                      -> VName+                      -> DistNestT m KernelsStms+segmentedUpdateKernel nest perm cs arr slice v = do+  (base_ispace, kernel_inps) <- flatKernel nest+  let slice_dims = sliceDims slice+  slice_gtids <- replicateM (length slice_dims) (newVName "gtid_slice")++  let ispace = base_ispace ++ zip slice_gtids slice_dims++  ((res_t, res), kstms) <- runBinder $ do++    -- Compute indexes into full array.+    v' <- certifying cs $+          letSubExp "v" $ BasicOp $ Index v $ map (DimFix . Var) slice_gtids+    let pexp = primExpFromSubExp int32+    slice_is <- traverse (letSubExp "index" <=< toExp) $+                fixSlice (map (fmap pexp) slice) $ map (pexp . Var) slice_gtids++    let write_is = map (Var . fst) base_ispace ++ slice_is+        arr' = maybe (error "incorrectly typed Update") kernelInputArray $+               find ((==arr) . kernelInputName) kernel_inps+    arr_t <- lookupType arr'+    v_t <- subExpType v'+    return (v_t,+            WriteReturns (arrayDims arr_t) arr' [(write_is, v')])++  mk_lvl <- mkSegLevel+  (k, prestms) <- mapKernel mk_lvl ispace kernel_inps [res_t] $+                  KernelBody () kstms [res]++  traverse renameStm <=< runBinder_ $ do+    addStms prestms++    let pat = Pattern [] $ rearrangeShape perm $+              patternValueElements $ loopNestingPattern $ fst nest++    letBind_ pat $ Op $ SegOp k+ segmentedHistKernel :: MonadFreshNames m =>                             KernelNest                          -> [Int]@@ -806,7 +865,7 @@    (ispace, kernel_inps) <- flatKernel nest -  unless (not $ free_in_op `namesIntersect` bound_by_nest) $+  when (free_in_op `namesIntersect` bound_by_nest) $     fail "Non-fold lambda uses nest-bound parameters."    let indices = map fst ispace
src/Futhark/Pass/ExtractKernels/Distribution.hs view
@@ -317,7 +317,7 @@         removeIdentityMappingGeneral bound_by_stms inner_pat inner_res   in (DistributionBody       { distributionTarget = Targets (inner_pat', inner_res') targets-      , distributionFreeInBody = fold (fmap freeIn stms) `namesSubtract` bound_by_stms+      , distributionFreeInBody = foldMap freeIn stms `namesSubtract` bound_by_stms       , distributionIdentityMap = inner_identity_map       , distributionExpandTarget = inner_expand_target       },
src/Futhark/Pass/ExtractKernels/ISRWIM.hs view
@@ -82,7 +82,7 @@             letSubExp "acc" $ BasicOp $ Index v $               fullSlice v_t [DimFix $ intConst Int32 0]           indexAcc Constant{} =-            fail "irwim: array accumulator is a constant."+            error "irwim: array accumulator is a constant."       accs' <- mapM indexAcc accs        let (_red_acc_params, red_elem_params) =
src/Futhark/Pass/KernelBabysitting.hs view
@@ -170,7 +170,7 @@         mapper ctx = identityMapper { mapOnBody = const (onBody ctx)                                     , mapOnOp = onOp ctx } -        mkSizeSubsts = fold . fmap mkStmSizeSubst+        mkSizeSubsts = foldMap mkStmSizeSubst           where mkStmSizeSubst (Let (Pattern [] [pe]) _ (Op (SizeOp (SplitSpace _ _ _ elems_per_i)))) =                   M.singleton (patElemName pe) elems_per_i                 mkStmSizeSubst _ = mempty
src/Futhark/Passes.hs view
@@ -35,6 +35,7 @@ standardPipeline =   passes [ simplifySOACS          , inlineAndRemoveDeadFunctions+         , simplifySOACS          , performCSE True          , simplifySOACS            -- We run fusion twice
src/Futhark/Representation/AST/Attributes.hs view
@@ -19,7 +19,6 @@   , builtInFunctions    -- * Extra tools-  , funDefByName   , asBasicOp   , safeExp   , subExpVars@@ -64,10 +63,6 @@ builtInFunctions :: M.Map Name (PrimType,[PrimType]) builtInFunctions = M.fromList $ map namify $ M.toList primFuns   where namify (k,(paramts,ret,_)) = (nameFromString k, (ret, paramts))---- | Find the function of the given name in the Futhark program.-funDefByName :: Name -> Prog lore -> Maybe (FunDef lore)-funDefByName fname = find ((fname ==) . funDefName) . progFunctions  -- | If the expression is a 'BasicOp', return that 'BasicOp', otherwise 'Nothing'. asBasicOp :: Exp lore -> Maybe (BasicOp lore)
src/Futhark/Representation/AST/Attributes/Names.hs view
@@ -138,7 +138,7 @@                      FreeAttr (ExpAttr lore)) =>                     Stms lore -> Result -> FV freeInStmsAndRes stms res =-  fvBind (boundByStms stms) $ fold (fmap freeIn' stms) <> freeIn' res+  fvBind (boundByStms stms) $ foldMap freeIn' stms <> freeIn' res  -- | A class indicating that we can obtain free variable information -- from values of this type.@@ -166,7 +166,7 @@   freeIn' (a,b,c) = freeIn' a <> freeIn' b <> freeIn' c  instance FreeIn a => FreeIn [a] where-  freeIn' = fold . fmap freeIn'+  freeIn' = foldMap freeIn'  instance (FreeAttr (ExpAttr lore),           FreeAttr (BodyAttr lore),@@ -214,7 +214,7 @@     freeIn' cs <> precomputed attr (freeIn' attr <> freeIn' e <> freeIn' pat)  instance FreeIn (Stm lore) => FreeIn (Stms lore) where-  freeIn' = fold . fmap freeIn'+  freeIn' = foldMap freeIn'  instance FreeIn Names where   freeIn' = fvNames@@ -235,6 +235,11 @@   freeIn' (Var v) = freeIn' v   freeIn' Constant{} = mempty +instance FreeIn Space where+  freeIn' (ScalarSpace d _) = freeIn' d+  freeIn' DefaultSpace = mempty+  freeIn' (Space _) = mempty+ instance FreeIn d => FreeIn (ShapeBase d) where   freeIn' = freeIn' . shapeDims @@ -244,7 +249,7 @@  instance FreeIn shape => FreeIn (TypeBase shape u) where   freeIn' (Array _ shape _) = freeIn' shape-  freeIn' (Mem _)           = mempty+  freeIn' (Mem s)           = freeIn' s   freeIn' (Prim _)          = mempty  instance FreeIn attr => FreeIn (Param attr) where@@ -307,7 +312,7 @@  -- | The names bound by the bindings. boundByStms :: Stms lore -> Names-boundByStms = fold . fmap boundByStm+boundByStms = foldMap boundByStm  -- | The names of the lambda parameters plus the index parameter. boundByLambda :: Lambda lore -> [VName]
src/Futhark/Representation/AST/Attributes/Scope.hs view
@@ -35,7 +35,6 @@ import Control.Monad.Reader import qualified Control.Monad.RWS.Strict import qualified Control.Monad.RWS.Lazy-import Data.Foldable import qualified Data.Map.Strict as M  import Futhark.Representation.AST.Annotations@@ -144,7 +143,7 @@   scopeOf = mconcat . map scopeOf  instance Scoped lore (Stms lore) where-  scopeOf = fold . fmap scopeOf+  scopeOf = foldMap scopeOf  instance Scoped lore (Stm lore) where   scopeOf = scopeOfPattern . stmPattern
src/Futhark/Representation/AST/Attributes/TypeOf.hs view
@@ -35,7 +35,6 @@        where  import Data.Maybe-import Data.Foldable import qualified Data.Set as S  import Futhark.Representation.AST.Syntax@@ -153,7 +152,7 @@   extendedScope (traverse subExpType res) bndscope   where bndscope = scopeOf stms         boundInLet (Let pat _ _) = S.fromList $ patternNames pat-        bound = S.toList $ fold $ fmap boundInLet stms+        bound = S.toList $ foldMap boundInLet stms  -- | Given the context and value merge parameters of a Futhark @loop@, -- produce the return type.
src/Futhark/Representation/AST/Pretty.hs view
@@ -77,28 +77,26 @@  instance Pretty Space where   ppr DefaultSpace = mempty-  ppr (Space s)    = text "@" <> text s+  ppr (Space s) = text "@" <> text s+  ppr (ScalarSpace d t) = text "@" <> brackets (mconcat $ map ppr d) <> ppr t  instance Pretty u => Pretty (TypeBase Shape u) where   ppr (Prim et) = ppr et   ppr (Array et (Shape ds) u) =     ppr u <> mconcat (map (brackets . ppr) ds) <> ppr et-  ppr (Mem DefaultSpace) = text "mem"-  ppr (Mem (Space sp)) = text "mem" <> text "@" <> text sp+  ppr (Mem s) = text "mem" <> ppr s  instance Pretty u => Pretty (TypeBase ExtShape u) where   ppr (Prim et) = ppr et   ppr (Array et (Shape ds) u) =     ppr u <> mconcat (map (brackets . ppr) ds) <> ppr et-  ppr (Mem DefaultSpace) = text "mem"-  ppr (Mem (Space sp)) = text "mem" <> text "@" <> text sp+  ppr (Mem s) = text "mem" <> ppr s  instance Pretty u => Pretty (TypeBase Rank u) where   ppr (Prim et) = ppr et   ppr (Array et (Rank n) u) =     ppr u <> mconcat (replicate n $ brackets mempty) <> ppr et-  ppr (Mem DefaultSpace) = text "mem"-  ppr (Mem (Space sp)) = text "mem" <> text "@" <> text sp+  ppr (Mem s) = text "mem" <> ppr s  instance Pretty Ident where   ppr ident = ppr (identType ident) <+> ppr (identName ident)@@ -268,7 +266,7 @@               | otherwise    = "fun"  instance PrettyLore lore => Pretty (Prog lore) where-  ppr = stack . punctuate line . map ppr . progFunctions+  ppr = stack . punctuate line . map ppr . progFuns  instance Pretty d => Pretty (DimChange d) where   ppr (DimCoercion se) = text "~" <> ppr se
src/Futhark/Representation/AST/Syntax.hs view
@@ -372,5 +372,5 @@                     deriving (Eq, Show, Ord)  -- | An entire Futhark program.-newtype Prog lore = Prog { progFunctions :: [FunDef lore] }+newtype Prog lore = Prog { progFuns :: [FunDef lore] }                     deriving (Eq, Ord, Show)
src/Futhark/Representation/AST/Syntax/Core.hs view
@@ -30,6 +30,7 @@          , Diet(..)          , ErrorMsg (..)          , ErrorMsgPart (..)+         , errorMsgArgTypes           -- * Values          , PrimValue(..)@@ -146,6 +147,10 @@ -- between host memory ('DefaultSpace') and GPU space. data Space = DefaultSpace            | Space SpaceId+           | ScalarSpace [SubExp] PrimType+             -- ^ A special kind of memory that is a statically sized+             -- array of some primitive type.  Used for private memory+             -- on GPUs.              deriving (Show, Eq, Ord)  -- | A string representing a specific non-default memory space.@@ -342,3 +347,10 @@ instance Traversable ErrorMsgPart where   traverse _ (ErrorString s) = pure $ ErrorString s   traverse f (ErrorInt32 a) = ErrorInt32 <$> f a++-- | How many non-constant parts does the error message have, and what+-- is their type?+errorMsgArgTypes :: ErrorMsg a -> [PrimType]+errorMsgArgTypes (ErrorMsg parts) = mapMaybe onPart parts+  where onPart ErrorString{} = Nothing+        onPart ErrorInt32{} = Just $ IntType Int32
src/Futhark/Representation/Aliases.hs view
@@ -41,7 +41,6 @@  import Control.Monad.Identity import Control.Monad.Reader-import Data.Foldable import Data.Maybe import qualified Data.Map.Strict as M @@ -295,7 +294,7 @@   -- bound in bnds.   let (aliases, consumed) = mkStmsAliases bnds res       boundNames =-        fold $ fmap (namesFromList . patternNames . stmPattern) bnds+        foldMap (namesFromList . patternNames . stmPattern) bnds       aliases' = map (`namesSubtract` boundNames) aliases       consumed' = consumed `namesSubtract` boundNames   in (map Names' aliases', Names' consumed')
src/Futhark/Representation/ExplicitMemory.hs view
@@ -76,13 +76,8 @@        , lookupMemInfo        , subExpMemInfo        , lookupArraySummary-       , fullyLinear-       , ixFunMatchesInnerShape        , existentialiseIxFun -       , scalarMemory-       , allScalarMemory-          -- * Module re-exports        , module Futhark.Representation.AST.Attributes        , module Futhark.Representation.AST.Traversals@@ -98,8 +93,9 @@ import Control.Monad.Reader import Control.Monad.Except import qualified Data.Map.Strict as M-import Data.Foldable (traverse_)+import Data.Foldable (traverse_, toList) import Data.List+import qualified Data.Set as S  import Futhark.Analysis.Metrics import Futhark.Representation.AST.Syntax@@ -115,7 +111,6 @@ import Futhark.Analysis.PrimExp.Convert import Futhark.Analysis.PrimExp.Simplify import Futhark.Util-import Futhark.Util.IntegralExp import qualified Futhark.Util.Pretty as PP import qualified Futhark.Optimise.Simplify.Engine as Engine import Futhark.Optimise.Simplify.Lore@@ -195,7 +190,7 @@  instance PP.Pretty inner => PP.Pretty (MemOp inner) where   ppr (Alloc e DefaultSpace) = PP.text "alloc" <> PP.apply [PP.ppr e]-  ppr (Alloc e (Space sp)) = PP.text "alloc" <> PP.apply [PP.ppr e, PP.text sp]+  ppr (Alloc e s) = PP.text "alloc" <> PP.apply [PP.ppr e, PP.ppr s]   ppr (Inner k) = PP.ppr k  instance OpMetrics inner => OpMetrics (MemOp inner) where@@ -280,7 +275,7 @@  instance (FreeIn d, FreeIn ret) => FreeIn (MemInfo d u ret) where   freeIn' (MemArray _ shape _ ret) = freeIn' shape <> freeIn' ret-  freeIn' MemMem{} = mempty+  freeIn' (MemMem s) = freeIn' s   freeIn' MemPrim{} = mempty  instance (Substitute d, Substitute ret) => Substitute (MemInfo d u ret) where@@ -321,10 +316,8 @@ instance (PP.Pretty (TypeBase (ShapeBase d) u),           PP.Pretty d, PP.Pretty u, PP.Pretty ret) => PP.Pretty (MemInfo d u ret) where   ppr (MemPrim bt) = PP.ppr bt-  ppr (MemMem DefaultSpace) =-    PP.text "mem"-  ppr (MemMem (Space sp)) =-    PP.text "mem" <> PP.text "@" <> PP.text sp+  ppr (MemMem DefaultSpace) = PP.text "mem"+  ppr (MemMem s) = PP.text "mem" <> PP.ppr s   ppr (MemArray bt shape u ret) =     PP.ppr (Array bt shape u) <> PP.text "@" <> PP.ppr ret @@ -418,9 +411,7 @@   ppr (ReturnsInBlock v ixfun) =     PP.parens $ PP.text (pretty v) <> PP.text "->" <> PP.ppr ixfun   ppr (ReturnsNewBlock space i ixfun) =-    PP.text ("?" ++ show i) <> space' <> PP.text "->" <> PP.ppr ixfun-    where space' = case space of DefaultSpace -> mempty-                                 Space s -> PP.text $ "@" ++ s+    PP.text ("?" ++ show i) <> PP.ppr space <> PP.text "->" <> PP.ppr ixfun  instance FreeIn MemReturn where   freeIn' (ReturnsInBlock v ixfun) = freeIn' v <> freeIn' ixfun@@ -562,25 +553,8 @@   let (ctx_ts, val_ts) = splitFromEnd (length rettype) ts       (ctx_res, _val_res) = splitFromEnd (length rettype) res -      getId :: (SubExp,Int) -> Maybe (VName,Int)-      getId (Var ii, i) = Just (ii,i)-      getId (Constant _, _) = Nothing--      (ctx_map_ids, ctx_map_exts) =-        getExtMaps $ mapMaybe getId $ zip ctx_res [0..length ctx_res - 1]-       existentialiseIxFun0 :: IxFun -> ExtIxFun-      existentialiseIxFun0 = IxFun.substituteInIxFun ctx_map_ids . fmap (fmap Free)--      getCt :: (Int,SubExp) -> Maybe (Ext VName, PrimExp (Ext VName))-      getCt (_, Var _) = Nothing-      getCt (i, Constant c) = Just (Ext i, ValueExp c)--      ctx_map_cts = M.fromList $ mapMaybe getCt $-                    zip [0..length ctx_res - 1] ctx_res--      substConstsInExtIndFun :: ExtIxFun -> ExtIxFun-      substConstsInExtIndFun = IxFun.substituteInIxFun (ctx_map_cts<>ctx_map_exts)+      existentialiseIxFun0 = fmap $ fmap Free        fetchCtx i = case maybeNth i $ zip ctx_res ctx_ts of                      Nothing -> throwError $ "Cannot find context variable " ++@@ -609,24 +583,25 @@           throwError $ unwords ["Expected ext dim", pretty i, "=>", pretty x,                                 "but got", pretty y] +      extsInMemInfo :: MemInfo ExtSize u MemReturn -> S.Set Int+      extsInMemInfo (MemArray _ shp _ ret) =+        extInShape shp <> extInMemReturn ret+      extsInMemInfo _ = S.empty+       checkMemReturn (ReturnsInBlock x_mem x_ixfun) (ArrayIn y_mem y_ixfun)-          | x_mem == y_mem = do-              let x_ixfun' = substConstsInExtIndFun x_ixfun-                  y_ixfun' = existentialiseIxFun0   y_ixfun-              unless (x_ixfun' == y_ixfun') $+          | x_mem == y_mem =+              unless (IxFun.closeEnough x_ixfun $ existentialiseIxFun0 y_ixfun) $                 throwError $ unwords  ["Index function unification failed (ReturnsInBlock)",-                    "\nixfun of body result: ", pretty y_ixfun',-                    "\nixfun of return type: ", pretty x_ixfun',+                    "\nixfun of body result: ", pretty y_ixfun,+                    "\nixfun of return type: ", pretty x_ixfun,                     "\nand context elements: ", pretty ctx_res]       checkMemReturn (ReturnsNewBlock x_space x_ext x_ixfun)                      (ArrayIn y_mem y_ixfun) = do         (x_mem, x_mem_type)  <- fetchCtx x_ext-        let x_ixfun' = substConstsInExtIndFun x_ixfun-            y_ixfun' = existentialiseIxFun0   y_ixfun-        unless (x_ixfun' == y_ixfun') $+        unless (IxFun.closeEnough x_ixfun $ existentialiseIxFun0 y_ixfun) $           throwError $ unwords  ["Index function unification failed (ReturnsNewBlock)",-            "\nixfun of body result: ", pretty y_ixfun',-            "\nixfun of return type: ", pretty x_ixfun',+            "\nixfun of body result: ", pretty y_ixfun,+            "\nixfun of return type: ", pretty x_ixfun,             "\nand context elements: ", pretty ctx_res]         case x_mem_type of           MemMem y_space -> do@@ -652,6 +627,13 @@                       , s                       ] +  unless (length (S.unions $ map extsInMemInfo rettype)  == length ctx_res) $+    TC.bad $ TC.TypeError $ "Too many context parameters for the number of " +++    "existentials in the return type! type:\n  " +++    prettyTuple rettype +++    "\ncannot match context parameters:\n  " ++ prettyTuple ctx_res++   either bad return =<< runExceptT (zipWithM_ checkReturn rettype val_ts)  matchPatternToExp :: (ExplicitMemorish lore) =>@@ -668,8 +650,11 @@       (ctx_map_ids, ctx_map_exts) =         getExtMaps $ zip ctx_ids [0..length ctx_ids - 1] +  let rt_exts = foldMap extInExpReturns rt+   unless (length val_ts == length rt &&-          and (zipWith (matches ctx_map_ids ctx_map_exts) val_ts rt)) $+          and (zipWith (matches ctx_map_ids ctx_map_exts) val_ts rt) &&+          M.keysSet ctx_map_exts `S.isSubsetOf` S.map Ext rt_exts) $     TC.bad $ TC.TypeError $ "Expression type:\n  " ++ prettyTuple rt ++                             "\ncannot match pattern type:\n  " ++ prettyTuple val_ts ++                             "\nwith context elements: " ++ pretty ctx_ids@@ -692,11 +677,33 @@              Just (ReturnsNewBlock y_space y_i y_ixfun)) ->               let x_ixfun' = IxFun.substituteInIxFun  ctxids x_ixfun                   y_ixfun' = IxFun.substituteInIxFun ctxexts y_ixfun-              in  x_space == y_space && x_i == y_i && x_ixfun' == y_ixfun'+              in  x_space == y_space && x_i == y_i && IxFun.closeEnough x_ixfun' y_ixfun'             (_, Nothing) -> True             _ -> False         matches _ _ _ _ = False +        extInExpReturns :: ExpReturns -> S.Set Int+        extInExpReturns (MemArray _ shape _ mem_return) =+          extInShape shape <> maybe S.empty extInMemReturn mem_return+        extInExpReturns _ = mempty+++extInShape :: ShapeBase (Ext SubExp) -> S.Set Int+extInShape shape = S.fromList $ mapMaybe isExt $ shapeDims shape++extInMemReturn :: MemReturn -> S.Set Int+extInMemReturn (ReturnsInBlock _ extixfn) = extInIxFn extixfn+extInMemReturn (ReturnsNewBlock _ i extixfn) =+  S.singleton i <> extInIxFn extixfn++extInIxFn :: ExtIxFun -> S.Set Int+extInIxFn ixfun = S.fromList $ concatMap (mapMaybe isExt . toList) ixfun++isExt :: Ext a -> Maybe Int+isExt (Ext i) = Just i+isExt _ = Nothing++ varMemInfo :: ExplicitMemorish lore =>               VName -> TC.TypeM lore (MemInfo SubExp NoUniqueness MemBind) varMemInfo name = do@@ -739,6 +746,7 @@                  VName -> MemInfo SubExp u MemBind              -> TC.TypeM lore () checkMemInfo _ (MemPrim _) = return ()+checkMemInfo _ (MemMem (ScalarSpace d _)) = mapM_ (TC.require [Prim int32]) d checkMemInfo _ (MemMem _) = return () checkMemInfo name (MemArray _ shape _ (ArrayIn v ixfun)) = do   t <- lookupType v@@ -1022,24 +1030,3 @@           where mem' = case M.lookup mem parammap of                   Just (Var v, _) -> v                   _               -> mem---- | Is an array of the given shape stored fully flat row-major with--- the given index function?-fullyLinear :: (Eq num, IntegralExp num) =>-               ShapeBase num -> IxFun.IxFun num -> Bool-fullyLinear shape ixfun =-  IxFun.isLinear ixfun && ixFunMatchesInnerShape shape ixfun--ixFunMatchesInnerShape :: (Eq num, IntegralExp num) =>-                          ShapeBase num -> IxFun.IxFun num -> Bool-ixFunMatchesInnerShape shape ixfun =-  drop 1 (IxFun.shape ixfun) == drop 1 (shapeDims shape)---- | Construct the scalar memory space corresponding to a given primitive type.-scalarMemory :: PrimType -> SpaceId-scalarMemory = ("scalar_"++) . pretty---- | A mapping from all scalar memory spaces to the 'PrimType' they--- store.-allScalarMemory :: M.Map SpaceId PrimType-allScalarMemory = M.fromList $ zip (map scalarMemory allPrimTypes) allPrimTypes
src/Futhark/Representation/ExplicitMemory/IndexFunction.hs view
@@ -21,6 +21,8 @@        , isDirect        , isLinear        , substituteInIxFun+       , leastGeneralGeneralization+       , closeEnough        )        where @@ -34,6 +36,8 @@ import Control.Monad.Writer import qualified Data.Map.Strict as M +import Futhark.Analysis.PrimExp (PrimExp(..))+import Futhark.Representation.AST.Syntax.Core (Ext(..)) import Futhark.Transform.Substitute import Futhark.Transform.Rename import Futhark.Representation.AST.Syntax@@ -41,15 +45,14 @@ import Futhark.Representation.AST.Attributes import Futhark.Util.IntegralExp import Futhark.Util.Pretty-import Futhark.Analysis.PrimExp.Convert-+import Futhark.Analysis.PrimExp.Convert (substituteInPrimExp)+import qualified Futhark.Analysis.PrimExp.Generalize as PEG --- | LMAD's representation consists of a permutation, a general offset, and, for--- each dimension a stride, rotate factor, number of elements, permutation, and--- monotonicity and unit-stride info for each dimension.  Note that the--- permutation is not strictly necessary in that the permutation can be--- performed directly on LMAD dimensions, but then it is difficult to extract--- the permutation back from an LMAD.+-- | LMAD's representation consists of a general offset and for each dimension a+-- stride, rotate factor, number of elements (or shape), permutation, and+-- monotonicity. Note that the permutation is not strictly necessary in that the+-- permutation can be performed directly on LMAD dimensions, but then it is+-- difficult to extract the permutation back from an LMAD. -- -- LMAD algebra is closed under composition w.r.t. operators such as permute, -- repeat, index and slice.  However, other operations, such as reshape, cannot@@ -62,9 +65,10 @@ -- However, we expect that the common case is when the index function is one -- LMAD -- we call this the 'nice' representation. ----- Finally, the list of LMADs is tupled with the shape of the original array,--- and with contiguous info, i.e., if we instantiate all the points of the--- current index function, do we get a contiguous memory interval?+-- Finally, the list of LMADs is kept in an @IxFun@ together with the shape of+-- the original array, and a bit to indicate whether the index function is+-- contiguous, i.e., if we instantiate all the points of the current index+-- function, do we get a contiguous memory interval? -- -- By definition, the LMAD denotes the set of points (simplified): --@@ -785,3 +789,68 @@                     Monotonicity -> LMADDim num -> Bool         isMonDim mon (LMADDim s r _ _ ldmon) =           s == 0 || ((ignore_rots || r == 0) && mon == ldmon)++-- | Generalization (anti-unification)+--+-- Anti-unification of two index functions is supported under the following conditions:+--   0. Both index functions are represented by ONE lmad (assumed common case!)+--   1. The support array of the two indexfuns have the same dimensionality+--      (we can relax this condition if we use a 1D support, as we probably should!)+--   2. The contiguous property and the per-dimension monotonicity are the same+--      (otherwise we might loose important information; this can be relaxed!)+--   3. Most importantly, both index functions correspond to the same permutation+--      (since the permutation is represented by INTs, this restriction cannot+--       be relaxed, unless we move to a gated-LMAD representation!)+--+-- `k0` is the existential to use for the shape of the array.+leastGeneralGeneralization :: Eq v => IxFun (PrimExp v) -> IxFun (PrimExp v) ->+                              Maybe (IxFun (PrimExp (Ext v)), [(PrimExp v, PrimExp v)])+leastGeneralGeneralization (IxFun (lmad1 :| []) oshp1 ctg1) (IxFun (lmad2 :| []) oshp2 ctg2) = do+  guard $+    length oshp1 == length oshp2 &&+    ctg1 == ctg2 &&+    map ldPerm (lmadDims lmad1) == map ldPerm (lmadDims lmad2) &&+    lmadDMon lmad1 == lmadDMon lmad2+  let (ctg, dperm, dmon) = (ctg1, lmadPermutation lmad1, lmadDMon lmad1)+  (dshp, m1) <- generalize [] (lmadDShp lmad1) (lmadDShp lmad2)+  (oshp, m2) <- generalize m1 oshp1 oshp2+  (dstd, m3) <- generalize m2 (lmadDSrd lmad1) (lmadDSrd lmad2)+  (drot, m4) <- generalize m3 (lmadDRot lmad1) (lmadDRot lmad2)+  (offt, m5) <- PEG.leastGeneralGeneralization m4 (lmadOffset lmad1) (lmadOffset lmad2)+  let lmad_dims = map (\(a,b,c,d,e) -> LMADDim a b c d e) $+        zip5 dstd drot dshp dperm dmon+      lmad = LMAD offt lmad_dims+  return (IxFun (lmad :| []) oshp ctg, m5)+  where lmadDMon = map ldMon    . lmadDims+        lmadDSrd = map ldStride . lmadDims+        lmadDShp = map ldShape  . lmadDims+        lmadDRot = map ldRotate . lmadDims+        generalize m l1 l2 =+          foldM (\(l_acc, m') (pe1,pe2) -> do+                    (e, m'') <- PEG.leastGeneralGeneralization m' pe1 pe2+                    return (l_acc++[e], m'')+                ) ([], m) (zip l1 l2)+leastGeneralGeneralization _ _ = Nothing++-- | When comparing index functions as part of the type check in ExplicitMemory,+-- we may run into problems caused by the simplifier. As index functions can be+-- generalized over if-then-else expressions, the simplifier might hoist some of+-- the code from inside the if-then-else (computing the offset of an array, for+-- instance), but now the type checker cannot verify that the generalized index+-- function is valid, because some of the existentials are computed somewhere+-- else. To Work around this, we've had to relax the ExplicitMemory type-checker+-- a bit, specifically, we've introduced this function to verify whether two+-- index functions are "close enough" that we can assume that they match. We use+-- this instead of `ixfun1 == ixfun2` and hope that it's good enough.+closeEnough :: IxFun num -> IxFun num -> Bool+closeEnough ixf1 ixf2 =+  (length (base ixf1) == length (base ixf2)) &&+  (ixfunContig ixf1 == ixfunContig ixf2) &&+  (NE.length (ixfunLMADs ixf1) == NE.length (ixfunLMADs ixf2)) &&+  all closeEnoughLMADs (NE.zip (ixfunLMADs ixf1) (ixfunLMADs ixf2))+  where+    closeEnoughLMADs :: (LMAD num, LMAD num) -> Bool+    closeEnoughLMADs (lmad1, lmad2) =+      length (lmadDims lmad1) == length (lmadDims lmad2) &&+      map ldPerm (lmadDims lmad1) ==+      map ldPerm (lmadDims lmad2)
src/Futhark/Representation/Kernels/Kernel.hs view
@@ -56,7 +56,6 @@ import Control.Monad.Writer hiding (mapM_) import Control.Monad.Identity hiding (mapM_) import qualified Data.Map.Strict as M-import Data.Foldable import Data.List  import Futhark.Representation.AST@@ -157,9 +156,16 @@ -- might otherwise be removed by the simplifier because they're -- semantically redundant.  This has no semantic effect and can be -- ignored at code generation.-data ResultManifest = ResultNoSimplify -- ^ Don't simplify this one!-                    | ResultMaySimplify -- ^ Go nuts.-                  deriving (Eq, Show, Ord)+data ResultManifest+  = ResultNoSimplify+    -- ^ Don't simplify this one!+  | ResultMaySimplify+    -- ^ Go nuts.+  | ResultPrivate+    -- ^ The results produced are only used within the+    -- same physical thread later on, and can thus be+    -- kept in registers.+  deriving (Eq, Show, Ord)  data KernelResult = Returns ResultManifest SubExp                     -- ^ Each "worker" in the kernel returns this.@@ -199,7 +205,7 @@ instance Attributes lore => FreeIn (KernelBody lore) where   freeIn' (KernelBody attr stms res) =     fvBind bound_in_stms $ freeIn' attr <> freeIn' stms <> freeIn' res-    where bound_in_stms = fold $ fmap boundByStm stms+    where bound_in_stms = foldMap boundByStm stms  instance Attributes lore => Substitute (KernelBody lore) where   substituteNames subst (KernelBody attr stms res) =@@ -322,6 +328,8 @@ instance Pretty KernelResult where   ppr (Returns ResultNoSimplify what) =     text "returns (manifest)" <+> ppr what+  ppr (Returns ResultPrivate what) =+    text "returns (private)" <+> ppr what   ppr (Returns ResultMaySimplify what) =     text "returns" <+> ppr what   ppr (WriteReturns rws arr res) =@@ -358,14 +366,6 @@               | SegGroup { segNumGroups :: Count NumGroups SubExp                          , segGroupSize :: Count GroupSize SubExp                          , segVirt :: SegVirt }-              | SegThreadScalar { segNumGroups :: Count NumGroups SubExp-                                , segGroupSize :: Count GroupSize SubExp-                                , segVirt :: SegVirt }-                -- ^ Like 'SegThread', but with the invariant that the-                -- results produced are only used within the same-                -- physical thread later on, and can thus be kept in-                -- registers.  May only occur within an enclosing-                -- 'SegGroup' construct.               deriving (Eq, Ord, Show)  -- | Index space of a 'SegOp'.@@ -461,8 +461,6 @@     namesFromList (concatMap histDest ops) <> consumedInKernelBody kbody  checkSegLevel :: Maybe SegLevel -> SegLevel -> TC.TypeM lore ()-checkSegLevel Nothing SegThreadScalar{} =-  TC.bad $ TC.TypeError "SegThreadScalar at top level." checkSegLevel Nothing _ =   return () checkSegLevel (Just SegThread{}) _ =@@ -646,11 +644,6 @@   <$> traverse (mapOnSegOpSubExp tv) num_groups   <*> traverse (mapOnSegOpSubExp tv) group_size   <*> pure virt-mapOnSegLevel tv (SegThreadScalar num_groups group_size virt) =-  SegThreadScalar-  <$> traverse (mapOnSegOpSubExp tv) num_groups-  <*> traverse (mapOnSegOpSubExp tv) group_size-  <*> pure virt  mapOnSegOpType :: Monad m =>                   SegOpMapper flore tlore m -> Type -> m Type@@ -734,7 +727,6 @@  instance PP.Pretty SegLevel where   ppr SegThread{} = "thread"-  ppr SegThreadScalar{} = "scalar"   ppr SegGroup{} = "group"  ppSegLevel :: SegLevel -> PP.Doc
src/Futhark/Representation/Kernels/Simplify.hs view
@@ -196,9 +196,6 @@   simplify (SegGroup num_groups group_size virt) =     SegGroup <$> traverse Engine.simplify num_groups <*>     traverse Engine.simplify group_size <*> pure virt-  simplify (SegThreadScalar num_groups group_size virt) =-    SegThreadScalar <$> traverse Engine.simplify num_groups <*>-    traverse Engine.simplify group_size <*> pure virt  instance Engine.Simplifiable SegSpace where   simplify (SegSpace phys dims) =@@ -227,9 +224,11 @@ kernelRules = standardRules <>               ruleBook [ RuleOp removeInvariantKernelResults                        , RuleOp mergeSegRedOps-                       , RuleOp redomapIotaToLoop ]+                       , RuleOp redomapIotaToLoop+                       ]                        [ RuleOp distributeKernelResults-                       , RuleBasicOp removeUnnecessaryCopy]+                       , RuleBasicOp removeUnnecessaryCopy+                       ]  -- If a kernel produces something invariant to the kernel, turn it -- into a replicate.@@ -237,10 +236,6 @@ removeInvariantKernelResults vtable (Pattern [] kpes) attr                              (SegOp (SegMap lvl space ts (KernelBody _ kstms kres))) = Simplify $ do -  case lvl of-    SegThreadScalar{} -> cannotSimplify-    _ -> return ()-   (ts', kpes', kres') <-     unzip3 <$> filterM checkForInvarianceResult (zip3 ts kpes kres) @@ -253,8 +248,9 @@   where isInvariant Constant{} = True         isInvariant (Var v) = isJust $ ST.lookup v vtable -        checkForInvarianceResult (_, pe, Returns _ se)-          | isInvariant se = do+        checkForInvarianceResult (_, pe, Returns rm se)+          | rm == ResultMaySimplify,+            isInvariant se = do               letBindNames_ [patElemName pe] $                 BasicOp $ Replicate (Shape $ segSpaceDims space) se               return False@@ -270,24 +266,32 @@   -- Iterate through the bindings.  For each, we check whether it is   -- in kres and can be moved outside.  If so, we remove it from kres   -- and kpes and make it a binding outside.-  (kpes', kts', kres', kstms_rev) <- localScope (scopeOfSegSpace space) $-    foldM distribute (kpes, kts, kres, []) kstms+  (kpes', kts', kres', kstms') <- localScope (scopeOfSegSpace space) $+    foldM distribute (kpes, kts, kres, mempty) kstms    when (kpes' == kpes)     cannotSimplify    addStm $ Let (Pattern [] kpes') attr $ Op $ SegOp $-    SegMap lvl space kts' $ mkWiseKernelBody () (stmsFromList $ reverse kstms_rev) kres'+    SegMap lvl space kts' $ mkWiseKernelBody () kstms' kres'   where-    free_in_kstms = fold $ fmap freeIn kstms+    free_in_kstms = foldMap freeIn kstms -    distribute (kpes', kts', kres', kstms_rev) bnd-      | Let (Pattern [] [pe]) _ (BasicOp (Index arr slice)) <- bnd,+    sliceWithGtidsFixed stm+      | Let _ _ (BasicOp (Index arr slice)) <- stm,         space_slice <- map (DimFix . Var . fst) $ unSegSpace space,         space_slice `isPrefixOf` slice,         remaining_slice <- drop (length space_slice) slice,         all (isJust . flip ST.lookup vtable) $ namesToList $-          freeIn arr <> freeIn remaining_slice,+          freeIn arr <> freeIn remaining_slice =+          Just (remaining_slice, arr)++      | otherwise =+          Nothing++    distribute (kpes', kts', kres', kstms') stm+      | Let (Pattern [] [pe]) _ _ <- stm,+        Just (remaining_slice, arr) <- sliceWithGtidsFixed stm,         Just (kpe, kpes'', kts'', kres'') <- isResult kpes' kts' kres' pe = do           let outer_slice = map (\d -> DimSlice                                        (constant (0::Int32))@@ -303,11 +307,11 @@             else index kpe           return (kpes'', kts'', kres'',                   if patElemName pe `nameIn` free_in_kstms-                  then bnd : kstms_rev-                  else kstms_rev)+                  then kstms' <> oneStm stm+                  else kstms') -    distribute (kpes', kts', kres', kstms_rev) bnd =-      return (kpes', kts', kres', bnd : kstms_rev)+    distribute (kpes', kts', kres', kstms') stm =+      return (kpes', kts', kres', kstms' <> oneStm stm)      isResult kpes' kts' kres' pe =       case partition matches $ zip3 kpes' kts' kres' of
src/Futhark/Representation/Primitive.hs view
@@ -833,12 +833,20 @@   , f32 "log10_32" (logBase 10), f64 "log10_64" (logBase 10)   , f32 "log2_32" (logBase 2), f64 "log2_64" (logBase 2)   , f32 "exp32" exp, f64 "exp64" exp+   , f32 "sin32" sin, f64 "sin64" sin+  , f32 "sinh32" sinh, f64 "sinh64" sinh   , f32 "cos32" cos, f64 "cos64" cos+  , f32 "cosh32" cosh, f64 "cosh64" cosh   , f32 "tan32" tan, f64 "tan64" tan+  , f32 "tanh32" tanh, f64 "tanh64" tanh   , f32 "asin32" asin, f64 "asin64" asin+  , f32 "asinh32" asinh, f64 "asinh64" asinh   , f32 "acos32" acos, f64 "acos64" acos+  , f32 "acosh32" acosh, f64 "acosh64" acosh   , f32 "atan32" atan, f64 "atan64" atan+  , f32 "atanh32" atanh, f64 "atanh64" atanh+   , f32 "round32" roundFloat, f64 "round64" roundDouble   , f32 "ceil32" ceilFloat, f64 "ceil64" ceilDouble   , f32 "floor32" floorFloat, f64 "floor64" floorDouble@@ -855,6 +863,56 @@   , i32 "popc32" $ IntValue . Int32Value . fromIntegral . popCount   , i64 "popc64" $ IntValue . Int32Value . fromIntegral . popCount +  , ("mad_hi8", ([IntType Int8, IntType Int8, IntType Int8], IntType Int8,+                 \case+                   [IntValue (Int8Value a), IntValue (Int8Value b), IntValue (Int8Value c)] ->+                     Just $ IntValue . Int8Value $ mad_hi8 (Int8Value a) (Int8Value b) c+                   _ -> Nothing+                ))+  , ("mad_hi16", ([IntType Int16, IntType Int16, IntType Int16], IntType Int16,+                 \case+                   [IntValue (Int16Value a), IntValue (Int16Value b), IntValue (Int16Value c)] ->+                     Just $ IntValue . Int16Value  $ mad_hi16 (Int16Value a) (Int16Value b) c+                   _ -> Nothing+                ))+  , ("mad_hi32", ([IntType Int32, IntType Int32, IntType Int32], IntType Int32,+                  \case+                   [IntValue (Int32Value a), IntValue (Int32Value b), IntValue (Int32Value c)] ->+                     Just $ IntValue . Int32Value  $ mad_hi32 (Int32Value a) (Int32Value b) c+                   _ -> Nothing+                ))+  , ("mad_hi64", ([IntType Int64, IntType Int64, IntType Int64], IntType Int64,+                  \case+                    [IntValue (Int64Value a), IntValue (Int64Value b), IntValue (Int64Value c)] ->+                      Just $ IntValue . Int64Value $ mad_hi64 (Int64Value a) (Int64Value b) c+                    _ -> Nothing+                ))++  , ("mul_hi8", ([IntType Int8, IntType Int8], IntType Int8,+                 \case+                   [IntValue (Int8Value a), IntValue (Int8Value b)] ->+                     Just $ IntValue . Int8Value $ mul_hi8 (Int8Value a) (Int8Value b)+                   _ -> Nothing+                ))+  , ("mul_hi16", ([IntType Int16, IntType Int16], IntType Int16,+                 \case+                   [IntValue (Int16Value a), IntValue (Int16Value b)] ->+                     Just $ IntValue . Int16Value  $ mul_hi16 (Int16Value a) (Int16Value b)+                   _ -> Nothing+                ))+  , ("mul_hi32", ([IntType Int32, IntType Int32], IntType Int32,+                  \case+                   [IntValue (Int32Value a), IntValue (Int32Value b)] ->+                     Just $ IntValue . Int32Value  $ mul_hi32 (Int32Value a) (Int32Value b)+                   _ -> Nothing+                ))+  , ("mul_hi64", ([IntType Int64, IntType Int64], IntType Int64,+                  \case+                    [IntValue (Int64Value a), IntValue (Int64Value b)] ->+                      Just $ IntValue . Int64Value $ mul_hi64 (Int64Value a) (Int64Value b)+                    _ -> Nothing+                ))+   , ("atan2_32",      ([FloatType Float32, FloatType Float32], FloatType Float32,       \case@@ -916,24 +974,15 @@           Just $ FloatValue $ Float64Value $ wordToDouble $ fromIntegral x         _ -> Nothing)) -  , ("lerp32",-     ([FloatType Float32, FloatType Float32, FloatType Float32], FloatType Float32,-      \case-        [FloatValue (Float32Value v0),-         FloatValue (Float32Value v1),-         FloatValue (Float32Value t)] ->-          Just $ FloatValue $ Float32Value $-          v0 + (v1-v0)*max 0 (min 1 t)-        _ -> Nothing))-  , ("lerp64",-     ([FloatType Float64, FloatType Float64, FloatType Float64], FloatType Float64,-      \case-        [FloatValue (Float64Value v0),-         FloatValue (Float64Value v1),-         FloatValue (Float64Value t)] ->-          Just $ FloatValue $ Float64Value $-          v0 + (v1-v0)*max 0 (min 1 t)-        _ -> Nothing))+  , f32_3 "lerp32" (\v0 v1 t -> v0 + (v1-v0)*max 0 (min 1 t))+  , f64_3 "lerp64" (\v0 v1 t -> v0 + (v1-v0)*max 0 (min 1 t))++  , f32_3 "mad32" (\a b c -> a*b+c)+  , f64_3 "mad64" (\a b c -> a*b+c)++  , f32_3 "fma32" (\a b c -> a*b+c)+  , f64_3 "fma64" (\a b c -> a*b+c)+   ]   where i8 s f = (s, ([IntType Int8], IntType Int32, i8PrimFun f))         i16 s f = (s, ([IntType Int16], IntType Int32, i16PrimFun f))@@ -941,6 +990,10 @@         i64 s f = (s, ([IntType Int64], IntType Int32, i64PrimFun f))         f32 s f = (s, ([FloatType Float32], FloatType Float32, f32PrimFun f))         f64 s f = (s, ([FloatType Float64], FloatType Float64, f64PrimFun f))+        f32_3 s f = (s, ([FloatType Float32,FloatType Float32,FloatType Float32],+                         FloatType Float32, f32PrimFun3 f))+        f64_3 s f = (s, ([FloatType Float64,FloatType Float64,FloatType Float64],+                         FloatType Float64, f64PrimFun3 f))          i8PrimFun f [IntValue (Int8Value x)] =           Just $ f x@@ -966,6 +1019,18 @@           Just $ FloatValue $ Float64Value $ f x         f64PrimFun _ _ = Nothing +        f32PrimFun3 f [FloatValue (Float32Value a),+                       FloatValue (Float32Value b),+                       FloatValue (Float32Value c)] =+          Just $ FloatValue $ Float32Value $ f a b c+        f32PrimFun3 _ _ = Nothing++        f64PrimFun3 f [FloatValue (Float64Value a),+                       FloatValue (Float64Value b),+                       FloatValue (Float64Value c)] =+          Just $ FloatValue $ Float64Value $ f a b c+        f64PrimFun3 _ _ = Nothing+ -- | Is the given value kind of zero? zeroIsh :: PrimValue -> Bool zeroIsh (IntValue k)                  = zeroIshInt k@@ -1134,3 +1199,39 @@ prettySigned :: Bool -> PrimType -> String prettySigned True (IntType it) = 'u' : drop 1 (pretty it) prettySigned _ t = pretty t++mul_hi8 :: IntValue -> IntValue -> Int8+mul_hi8 a b =+  let a' = intToWord64 a+      b' = intToWord64 b+  in fromIntegral (shiftR (a' * b') 8)++mul_hi16 :: IntValue -> IntValue -> Int16+mul_hi16 a b =+  let a' = intToWord64 a+      b' = intToWord64 b+  in fromIntegral (shiftR (a' * b') 16)++mul_hi32 :: IntValue -> IntValue -> Int32+mul_hi32 a b =+  let a' = intToWord64 a+      b' = intToWord64 b+  in fromIntegral (shiftR (a' * b') 32)++mul_hi64 :: IntValue -> IntValue -> Int64+mul_hi64 a b =+  let a' = (toInteger . intToWord64) a+      b' = (toInteger . intToWord64) b+  in fromIntegral (shiftR (a' * b') 64)++mad_hi8 :: IntValue -> IntValue -> Int8 -> Int8+mad_hi8 a b c = mul_hi8 a b + c++mad_hi16 :: IntValue -> IntValue -> Int16 -> Int16+mad_hi16 a b c = mul_hi16 a b + c++mad_hi32 :: IntValue -> IntValue -> Int32 -> Int32+mad_hi32 a b c = mul_hi32 a b + c++mad_hi64 :: IntValue -> IntValue -> Int64 -> Int64+mad_hi64 a b c = mul_hi64 a b + c
src/Futhark/Representation/Ranges.hs view
@@ -34,7 +34,6 @@  import Control.Monad.Identity import Control.Monad.Reader-import Data.Foldable  import Futhark.Representation.AST.Syntax import Futhark.Representation.AST.Attributes@@ -166,7 +165,7 @@ mkBodyRanges :: Stms lore -> Result -> [Range] mkBodyRanges bnds = map $ removeUnknownBounds . rangeOf   where boundInBnds =-          fold $ fmap (namesFromList . patternNames . stmPattern) bnds+          foldMap (namesFromList . patternNames . stmPattern) bnds         removeUnknownBounds (lower,upper) =           (removeUnknownBound lower,            removeUnknownBound upper)
src/Futhark/Test/Values.hs view
@@ -497,8 +497,7 @@  compareValue :: Int -> Value -> Value -> [Mismatch] compareValue i got_v expected_v-  | product (valueShape got_v) == 0 && product (valueShape expected_v) == 0-    || valueShape got_v == valueShape expected_v =+  | valueShape got_v == valueShape expected_v =     case (got_v, expected_v) of       (Int8Value _ got_vs, Int8Value _ expected_vs) ->         compareNum 1 got_vs expected_vs@@ -556,6 +555,7 @@ minTolerance :: Fractional a => a minTolerance = 0.002 -- 0.2% -tolerance :: (Ord a, Fractional a, UVec.Unbox a) => Vector a -> a-tolerance = UVec.foldl tolerance' minTolerance+tolerance :: (RealFloat a, UVec.Unbox a) => Vector a -> a+tolerance = UVec.foldl tolerance' minTolerance . UVec.filter (not . nanOrInf)   where tolerance' t v = max t $ minTolerance * v+        nanOrInf x = isInfinite x || isNaN x
src/Futhark/Transform/Rename.hs view
@@ -58,7 +58,7 @@ renameProg :: (Renameable lore, MonadFreshNames m) =>               Prog lore -> m (Prog lore) renameProg prog = modifyNameSource $-                  runRenamer $ Prog <$> mapM rename (progFunctions prog)+                  runRenamer $ Prog <$> mapM rename (progFuns prog)  -- | Rename bound variables such that each is unique.  The semantics -- of the expression is unaffected, under the assumption that the
src/Futhark/TypeCheck.hs view
@@ -464,10 +464,10 @@         local (\env -> env { envFtable = ftable }) $         checkFun fun   (ftable, _) <- runTypeM typeenv buildFtable-  sequence_ $ parMap rpar (onFunction ftable) $ progFunctions prog+  sequence_ $ parMap rpar (onFunction ftable) $ progFuns prog   where     buildFtable = do table <- initialFtable prog-                     foldM expand table $ progFunctions prog+                     foldM expand table $ progFuns prog     expand ftable (FunDef _ name ret params _)       | M.member name ftable =           bad $ DupDefinitionError name@@ -746,8 +746,8 @@ checkBasicOp (Concat i arr1exp arr2exps ressize) = do   arr1t  <- checkArrIdent arr1exp   arr2ts <- mapM checkArrIdent arr2exps-  let success = all (== (dropAt i 1 $ arrayDims arr1t)) $-                map (dropAt i 1 . arrayDims) arr2ts+  let success = all ((== dropAt i 1 (arrayDims arr1t)).+                     dropAt i 1 . arrayDims) arr2ts   unless success $     bad $ TypeError $     "Types of arguments to concat do not match.  Got " ++@@ -876,7 +876,8 @@  checkType :: Checkable lore =>              TypeBase Shape u -> TypeM lore ()-checkType = mapM_ checkSubExp . arrayDims+checkType (Mem (ScalarSpace d _)) = mapM_ (require [Prim int32]) d+checkType t = mapM_ checkSubExp $ arrayDims t  checkExtType :: Checkable lore =>                 TypeBase ExtShape u
src/Futhark/Util/Pretty.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS_GHC -fno-warn-orphans #-} -- | A re-export of the prettyprinting library, along with a convenience function. module Futhark.Util.Pretty        ( module Text.PrettyPrint.Mainland@@ -12,6 +13,8 @@        , oneLine        , annot        , nestedBlock+       , textwrap+       , shorten        )        where @@ -54,6 +57,10 @@ oneLine :: PP.Doc -> PP.Doc oneLine s = PP.text $ PP.displayS (PP.renderCompact s) "" +-- | Like 'text', but splits the string into words and permits line breaks between all of them.+textwrap :: String -> Doc+textwrap = folddoc (<+/>) . map text . words+ -- | Stack and prepend a list of 'Doc's to another 'Doc', separated by -- a linebreak.  If the list is empty, the second 'Doc' will be -- returned without a preceding linebreak.@@ -67,3 +74,11 @@ nestedBlock pre post body = text pre </>                             PP.indent 2 body </>                             text post++-- | Prettyprint on a single line up to at most some appropriate+-- number of characters, with trailing ... if necessary.  Used for+-- error messages.+shorten :: Pretty a => a -> Doc+shorten a | length s > 70 = text (take 70 s) <> text "..."+          | otherwise = text s+  where s = pretty a
src/Language/Futhark/Attributes.hs view
@@ -30,6 +30,8 @@    -- * Queries on patterns and params   , patternIdents+  , patternNames+  , patternMap   , patternType   , patternStructType   , patternParam@@ -62,11 +64,13 @@   , setAliases   , addAliases   , setUniqueness-  , removeShapeAnnotations-  , vacuousShapeAnnotations-  , anyDimShapeAnnotations+  , noSizes+  , addSizes+  , anySizes   , traverseDims   , DimPos(..)+  , mustBeExplicit+  , mustBeExplicitInType   , tupleRecord   , isTupleRecord   , areTupleFields@@ -75,7 +79,9 @@   , sortFields   , sortConstrs   , isTypeParam+  , isSizeParam   , combineTypeShapes+  , matchDims   , unscopeType   , onRecordField @@ -100,6 +106,7 @@   )   where +import           Control.Monad.State import           Control.Monad.Writer  hiding (Sum) import           Data.Char import           Data.Foldable@@ -138,11 +145,11 @@   case t of Array _ _ a ds ->               nub $ nestedDims (Scalar a) <> shapeDims ds             Scalar (Record fs) ->-              nub $ fold $ fmap nestedDims fs+              nub $ foldMap nestedDims fs             Scalar Prim{} ->               mempty             Scalar (Sum cs) ->-              nub $ fold $ (fmap . concatMap) nestedDims cs+              nub $ foldMap (concatMap nestedDims) cs             Scalar (Arrow _ v t1 t2) ->               filter (notV v) $ nestedDims t1 <> nestedDims t2             Scalar (TypeVar _ _ _ targs) ->@@ -155,22 +162,16 @@         notV (Named v) = (/=NamedDim (qualName v))  -- | Change the shape of a type to be just the 'Rank'.-removeShapeAnnotations :: TypeBase (DimDecl vn) as -> TypeBase () as-removeShapeAnnotations = modifyShapeAnnotations $ const ()+noSizes :: TypeBase (DimDecl vn) as -> TypeBase () as+noSizes = first $ const ()  -- | Add size annotations that are all 'AnyDim'.-vacuousShapeAnnotations :: TypeBase () as -> TypeBase (DimDecl vn) as-vacuousShapeAnnotations = modifyShapeAnnotations $ const AnyDim+addSizes :: TypeBase () as -> TypeBase (DimDecl vn) as+addSizes = first $ const AnyDim  -- | Change all size annotations to be 'AnyDim'.-anyDimShapeAnnotations :: TypeBase (DimDecl vn) as -> TypeBase (DimDecl vn) as-anyDimShapeAnnotations = modifyShapeAnnotations $ const AnyDim---- | Change the size annotations of a type.-modifyShapeAnnotations :: (oldshape -> newshape)-                       -> TypeBase oldshape as-                       -> TypeBase newshape as-modifyShapeAnnotations = first+anySizes :: TypeBase (DimDecl vn) as -> TypeBase (DimDecl vn) as+anySizes = first $ const AnyDim  -- | Where does this dimension occur? data DimPos@@ -206,6 +207,35 @@         onTypeArg b (TypeArgType t loc) =           TypeArgType <$> go b t <*> pure loc +mustBeExplicitAux :: StructType -> M.Map VName Bool+mustBeExplicitAux t =+  execState (traverseDims onDim t) mempty+  where onDim PosImmediate (NamedDim d) =+          modify $ \s -> M.insertWith (&&) (qualLeaf d) False s+        onDim _ (NamedDim d) =+          modify $ M.insertWith (&&) (qualLeaf d) True+        onDim _ _ =+          return ()++-- | Figure out which of the sizes in a parameter type must be passed+-- explicitly, because their first use is as something else than just+-- an array dimension.  'mustBeExplicit' is like this function, but+-- first decomposes into parameter types.+mustBeExplicitInType :: StructType -> S.Set VName+mustBeExplicitInType t =+  S.fromList $ M.keys $ M.filter id $ mustBeExplicitAux t++-- | Figure out which of the sizes in a binding type must be passed+-- explicitly, because their first use is as something else than just+-- an array dimension.+mustBeExplicit :: StructType -> S.Set VName+mustBeExplicit bind_t =+  let (ts, ret) = unfoldFunType bind_t+      alsoRet = M.unionWith (&&) $+                M.fromList $ zip (S.toList $ typeDimNames ret) $ repeat True+  in S.fromList $ M.keys $ M.filter id $ alsoRet $ foldl' onType mempty ts+  where onType uses t = uses <> mustBeExplicitAux t -- Left-biased union.+ -- | Return the uniqueness of a type. uniqueness :: TypeBase shape as -> Uniqueness uniqueness (Array _ u _ _) = u@@ -225,19 +255,20 @@ -- | @diet t@ returns a description of how a function parameter of -- type @t@ might consume its argument. diet :: TypeBase shape as -> Diet-diet (Scalar (Record ets))      = RecordDiet $ fmap diet ets-diet (Scalar (Prim _))          = Observe-diet (Scalar TypeVar{})         = Observe-diet (Scalar (Arrow _ _ t1 t2)) = FuncDiet (diet t1) (diet t2)-diet (Array _ Unique _ _)       = Consume-diet (Array _ Nonunique _ _)    = Observe-diet (Scalar Sum{})             = Observe+diet (Scalar (Record ets))              = RecordDiet $ fmap diet ets+diet (Scalar (Prim _))                  = Observe+diet (Scalar (Arrow _ _ t1 t2))         = FuncDiet (diet t1) (diet t2)+diet (Array _ Unique _ _)               = Consume+diet (Array _ Nonunique _ _)            = Observe+diet (Scalar (TypeVar _ Unique _ _))    = Consume+diet (Scalar (TypeVar _ Nonunique _ _)) = Observe+diet (Scalar Sum{})                     = Observe  -- | Convert any type to one that has rank information, no alias -- information, and no embedded names. toStructural :: TypeBase dim as              -> TypeBase () ()-toStructural = flip setAliases () . modifyShapeAnnotations (const ())+toStructural = flip setAliases () . first (const ())  -- | Remove aliasing information from a type. toStruct :: TypeBase dim as@@ -336,6 +367,9 @@ isTypeParam TypeParamType{}       = True isTypeParam TypeParamDim{}        = False +isSizeParam :: TypeParamBase vn -> Bool+isSizeParam = not . isTypeParam+ -- | Combine the shape information of types as much as possible. The first -- argument is the orignal type and the second is the type of the transformed -- expression. This is necessary since the original type may contain additional@@ -345,13 +379,44 @@ combineTypeShapes (Scalar (Record ts1)) (Scalar (Record ts2))   | M.keys ts1 == M.keys ts2 =       Scalar $ Record $ M.map (uncurry combineTypeShapes) (M.intersectionWith (,) ts1 ts2)-combineTypeShapes (Array als1 u1 et1 shape1) (Array als2 _u2 et2 shape2)-  | Just new_shape <- unifyShapes shape1 shape2 =-      arrayOfWithAliases (combineTypeShapes (Scalar et1) (Scalar et2)-                          `setAliases` mempty)-      (als1<>als2) new_shape u1+combineTypeShapes (Scalar (Arrow als1 p1 a1 b1)) (Scalar (Arrow als2 _p2 a2 b2)) =+  Scalar $ Arrow (als1<>als2) p1 (combineTypeShapes a1 a2) (combineTypeShapes b1 b2)+combineTypeShapes (Array als1 u1 et1 shape1) (Array als2 _u2 et2 _shape2) =+  arrayOfWithAliases (combineTypeShapes (Scalar et1) (Scalar et2)+                       `setAliases` mempty)+  (als1<>als2) shape1 u1 combineTypeShapes _ new_tp = new_tp +-- | Match the dimensions of otherwise assumed-equal types.+matchDims :: (Monoid as, Monad m) =>+             (d1 -> d2 -> m d1)+          -> TypeBase d1 as -> TypeBase d2 as+          -> m (TypeBase d1 as)+matchDims onDims t1 t2 =+  case (t1, t2) of+    (Array als1 u1 et1 shape1, Array als2 u2 et2 shape2) ->+      flip setAliases (als1<>als2) <$>+      (arrayOf <$>+       matchDims onDims (Scalar et1) (Scalar et2) <*>+       onShapes shape1 shape2 <*> pure (min u1 u2))+    (Scalar (Record f1), Scalar (Record f2)) ->+      Scalar . Record <$>+      traverse (uncurry (matchDims onDims)) (M.intersectionWith (,) f1 f2)+    (Scalar (Arrow als1 p1 a1 b1), Scalar (Arrow als2 _p2 a2 b2)) ->+      Scalar <$>+      (Arrow (als1 <> als2) p1 <$> matchDims onDims a1 a2 <*> matchDims onDims b1 b2)+    (Scalar (TypeVar als1 u v targs1),+     Scalar (TypeVar als2 _ _ targs2)) ->+      Scalar . TypeVar (als1 <> als2) u v <$> zipWithM matchTypeArg targs1 targs2+    _ -> return t1++  where matchTypeArg ta@TypeArgType{} _ = return ta+        matchTypeArg a _ = return a++        onShapes shape1 shape2 =+          ShapeDecl <$> zipWithM onDims (shapeDims shape1) (shapeDims shape2)++ -- | Set the uniqueness attribute of a type.  If the type is a record -- or sum type, the uniqueness of its components will be modified. setUniqueness :: TypeBase dim as -> Uniqueness -> TypeBase dim as@@ -414,7 +479,7 @@ -- reference the bound variables, and turn any dimensions that name -- them into AnyDim instead. unscopeType :: S.Set VName -> PatternType -> PatternType-unscopeType bound_here t = modifyShapeAnnotations onDim $ t `addAliases` S.map unbind+unscopeType bound_here t = first onDim $ t `addAliases` S.map unbind   where unbind (AliasBound v) | v `S.member` bound_here = AliasFree v         unbind a = a         onDim (NamedDim qn) | qualLeaf qn `S.member` bound_here = AnyDim@@ -448,51 +513,44 @@           M.singleton (baseName name) $ t           `addAliases` S.insert (AliasBound name) typeOf (ArrayLit _ (Info t) _) = t-typeOf (StringLit _ _) =-  Array mempty Unique (Prim (Unsigned Int8)) (ShapeDecl [AnyDim])-typeOf (Range _ _ _ (Info t) _) = t-typeOf (BinOp _ _ _ _ (Info t) _) = t+typeOf (StringLit vs _) =+  Array mempty Unique (Prim (Unsigned Int8))+  (ShapeDecl [ConstDim $ genericLength vs])+typeOf (Range _ _ _ (Info t, _) _) = t+typeOf (BinOp _ _ _ _ (Info t) _ _) = t typeOf (Project _ _ (Info t) _) = t-typeOf (If _ _ _ (Info t) _) = t+typeOf (If _ _ _ (Info t, _) _) = t typeOf (Var _ (Info t) _) = t-typeOf (Ascript _ _ (Info t) _) = t-typeOf (Apply _ _ _ (Info t) _) = t+typeOf (Ascript e _ _) = typeOf e+typeOf (Coerce _ _ (Info t, _) _) = t+typeOf (Apply _ _ _ (Info t, _) _) = t typeOf (Negate e _) = typeOf e-typeOf (LetPat _ _ _ (Info t) _) = t-typeOf (LetFun name _ body _) = unscopeType (S.singleton name) $ typeOf body+typeOf (LetPat _ _ _ (Info t, _) _) = t+typeOf (LetFun _ _ _ (Info t) _) = t typeOf (LetWith _ _ _ _ _ (Info t) _) = t-typeOf (Index _ _ (Info t) _) = t+typeOf (Index _ _ (Info t, _) _) = t typeOf (Update e _ _ _) = typeOf e `setAliases` mempty typeOf (RecordUpdate _ _ _ (Info t) _) = t typeOf (Unsafe e _) = typeOf e typeOf (Assert _ e _ _) = typeOf e-typeOf (DoLoop pat _ form _ _) =-  -- We set all of the uniqueness to be unique.  This is intentional,-  -- and matches what happens for function calls.  Those arrays that-  -- really *cannot* be consumed will alias something unconsumable,-  -- and will be caught that way.-  second (`S.difference` S.map AliasBound bound_here) $-  patternType pat `setUniqueness` Unique-  where bound_here = S.map identName (patternIdents pat) <> form_bound-        form_bound =-          case form of-            For v _ -> S.singleton $ identName v-            ForIn forpat _ -> S.map identName (patternIdents forpat)-            While{} -> mempty+typeOf (DoLoop _ _ _ _ _ (Info (t, _)) _) = t typeOf (Lambda params _ _ (Info (als, t)) _) =   unscopeType bound_here $ foldr (arrow . patternParam) t params `setAliases` als-  where bound_here = S.map identName (mconcat $ map patternIdents params)+  where bound_here = S.map identName (mconcat $ map patternIdents params) `S.difference`+                     S.fromList (mapMaybe (named . patternParam) params)         arrow (px, tx) y = Scalar $ Arrow () px tx y+        named (Named x, _) = Just x+        named (Unnamed, _) = Nothing typeOf (OpSection _ (Info t) _) =   t-typeOf (OpSectionLeft _ _ _ (_, Info pt2) (Info ret) _)  =+typeOf (OpSectionLeft _ _ _ (_, Info pt2) (Info ret, _) _)  =   foldFunType [fromStruct pt2] ret typeOf (OpSectionRight _ _ _ (Info pt1, _) (Info ret) _) =   foldFunType [fromStruct pt1] ret typeOf (ProjectSection _ (Info t) _) = t typeOf (IndexSection _ (Info t) _) = t typeOf (Constr _ _ (Info t) _)  = t-typeOf (Match _ cs (Info t) _) =+typeOf (Match _ cs (Info t, _) _) =   unscopeType (foldMap unscopeSet cs) t   where unscopeSet (CasePat p _ _) = S.map identName $ patternIdents p @@ -577,6 +635,23 @@ patternIdents PatternLit{}              = mempty patternIdents (PatternConstr _ _ ps _ ) = mconcat $ map patternIdents ps +-- | The set of names bound in a pattern.+patternNames :: (Functor f, Ord vn) => PatternBase f vn -> S.Set vn+patternNames (Id v _ _)                = S.singleton v+patternNames (PatternParens p _)       = patternNames p+patternNames (TuplePattern pats _)     = mconcat $ map patternNames pats+patternNames (RecordPattern fs _)      = mconcat $ map (patternNames . snd) fs+patternNames Wildcard{}                = mempty+patternNames (PatternAscription p _ _) = patternNames p+patternNames PatternLit{}              = mempty+patternNames (PatternConstr _ _ ps _ ) = mconcat $ map patternNames ps++-- | A mapping from names bound in a map to their identifier.+patternMap :: (Functor f) => PatternBase f VName -> M.Map VName (IdentBase f VName)+patternMap pat =+  M.fromList $ zip (map identName idents) idents+  where idents = S.toList $ patternIdents pat+ -- | The type of values bound by the pattern. patternType :: PatternBase Info VName -> PatternType patternType (Wildcard (Info t) _)          = t@@ -933,7 +1008,7 @@ -- syntactical properties. leadingOperator :: Name -> BinOp leadingOperator s = maybe Backtick snd $ find ((`isPrefixOf` s') . fst) $-                    sortBy (flip $ comparing $ length . fst) $+                    sortOn (Down . length . fst) $                     zip (map pretty operators) operators   where s' = nameToString s         operators :: [BinOp]
src/Language/Futhark/Core.hs view
@@ -10,6 +10,7 @@    -- * Location utilities   , locStr+  , locStrRel   , prettyStacktrace    -- * Name handling@@ -24,6 +25,7 @@   , baseString   , pretty   , quote+  , pquote    -- * Special identifiers   , defaultEntryPoint@@ -124,6 +126,24 @@           first_part ++ "-" ++ show line2 ++ ":" ++ show col2       where first_part = file ++ ":" ++ show line1 ++ ":" ++ show col1 ++-- | Like 'locStr', but @locStrRel prev now@ prints the location @now@+-- with the file name left out if the same as @prev@.  This is useful+-- when printing messages that are all in the context of some+-- initially printed location (e.g. the first mention contains the+-- file name; the rest just line and column name).+locStrRel :: (Located a, Located b) => a -> b -> String+locStrRel a b =+  case (locOf a, locOf b) of+    (Loc (Pos a_file _ _ _) _, Loc (Pos b_file line1 col1 _) (Pos _ line2 col2 _))+      | a_file == b_file,+        line1 == line2 ->+          first_part ++ "-" ++ show col2+      | a_file == b_file ->+          first_part ++ "-" ++ show line2 ++ ":" ++ show col2+      where first_part = show line1 ++ ":" ++ show col1+    _ -> locStr b+ -- | Given a list of strings representing entries in the stack trace -- and the index of the frame to highlight, produce a final -- newline-terminated string for showing to the user.  This string@@ -165,4 +185,8 @@ -- These are picked to not collide with characters permitted in -- identifiers. quote :: String -> String-quote s = "`" ++ s ++ "`"+quote s = "\"" ++ s ++ "\""++-- | As 'quote', but works on prettyprinted representation.+pquote :: Doc -> Doc+pquote = dquotes
− src/Language/Futhark/Futlib.hs
@@ -1,27 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TemplateHaskell #-}--- | The Futhark basis library embedded embedded as strings read during--- compilation of the Futhark compiler.  The advantage is that the--- standard library can be accessed without reading it from disk, thus--- saving users from include path headaches.-module Language.Futhark.Futlib (futlib, prelude) where--import Data.FileEmbed-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified System.FilePath.Posix as Posix--import Futhark.Util (toPOSIX)---- | Futlib embedded as 'T.Text' values, one for every file.-futlib :: [(Posix.FilePath, T.Text)]-futlib = map fixup futlib_bs-  where futlib_bs = $(embedDir "futlib")-        fixup (path, s) = ("/futlib" Posix.</> toPOSIX path, T.decodeUtf8 s)---- The files intended to be implicitly imported into every Futhark--- program.  Make sure it does not depend on anything too big to be--- serialised efficiently.-prelude :: [String]-prelude = map ("/futlib/"++) ["prelude"]
src/Language/Futhark/Interpreter.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveTraversable #-} module Language.Futhark.Interpreter   ( Ctx(..)   , Env@@ -10,17 +12,18 @@   , interpretFunction   , ExtOp(..)   , StackFrame(..)-  , typeEnv+  , typeCheckerEnv   , Value (ValuePrim, ValueArray, ValueRecord)   , fromTuple   , isEmptyArray+  , prettyEmptyArray   ) where  import Control.Monad.Trans.Maybe import Control.Monad.Free.Church import Control.Monad.Except+import Control.Monad.State import Control.Monad.Reader-import qualified Control.Monad.Fail as Fail import Data.Array import Data.Bifunctor (first) import Data.List hiding (break)@@ -30,7 +33,7 @@ import Data.Monoid hiding (Sum) import Data.Loc -import Language.Futhark hiding (Value)+import Language.Futhark hiding (Value, matchDims) import qualified Language.Futhark as F import Futhark.Representation.Primitive (intValue, floatValue) import qualified Futhark.Representation.Primitive as P@@ -59,18 +62,18 @@  type Stack = [StackFrame] +type Sizes = M.Map VName Int32+ -- | The monad in which evaluation takes place. newtype EvalM a = EvalM (ReaderT (Stack, M.Map FilePath Env)-                         (F ExtOp) a)+                         (StateT Sizes (F ExtOp)) a)   deriving (Monad, Applicative, Functor,             MonadFree ExtOp,-            MonadReader (Stack, M.Map FilePath Env))--instance Fail.MonadFail EvalM where-  fail = error+            MonadReader (Stack, M.Map FilePath Env),+            MonadState Sizes)  runEvalM :: M.Map FilePath Env -> EvalM a -> F ExtOp a-runEvalM imports (EvalM m) = runReaderT m (mempty, imports)+runEvalM imports (EvalM m) = evalStateT (runReaderT m (mempty, imports)) mempty  stacking :: SrcLoc -> Env -> EvalM a -> EvalM a stacking loc env = local $ \(ss, imports) ->@@ -87,20 +90,15 @@ lookupImport :: FilePath -> EvalM (Maybe Env) lookupImport f = asks $ M.lookup f . snd --- | A fully evaluated Futhark value.-data Value = ValuePrim !PrimValue-           | ValueArray !(Array Int Value)-           | ValueRecord (M.Map Name Value)-           | ValueFun (Value -> EvalM Value)-           | ValueSum Name [Value]+putExtSize :: VName -> Int32 -> EvalM ()+putExtSize v x = modify $ M.insert v x -instance Eq Value where-  ValuePrim x == ValuePrim y = x == y-  ValueArray x == ValueArray y = x == y-  ValueRecord x == ValueRecord y = x == y-  (ValueSum n1 vs1) == (ValueSum n2 vs2) = n1 == n2 && vs1 == vs2-  _ == _ = False+getSizes :: EvalM Sizes+getSizes = get +extSizeEnv :: EvalM Env+extSizeEnv = i32Env <$> getSizes+ prettyRecord :: Pretty a => M.Map Name a -> Doc prettyRecord m   | Just vs <- areTupleFields m =@@ -109,55 +107,175 @@       braces $ commasep $ map field $ M.toList m       where field (k, v) = ppr k <+> equals <+> ppr v +valueStructType :: ValueType -> StructType+valueStructType = first (ConstDim . fromIntegral)++-- | A shape is a tree to accomodate the case of records.  It is+-- parameterised over the representation of dimensions.+data Shape d = ShapeDim d (Shape d)+             | ShapeLeaf+             | ShapeRecord (M.Map Name (Shape d))+             | ShapeSum (M.Map Name [Shape d])+             deriving (Eq, Show, Functor, Foldable, Traversable)++type ValueShape = Shape Int32++instance Pretty d => Pretty (Shape d) where+  ppr ShapeLeaf = mempty+  ppr (ShapeDim d s) = brackets (ppr d) <> ppr s+  ppr (ShapeRecord m) = prettyRecord m+  ppr (ShapeSum cs) =+    mconcat (punctuate (text " | ") cs')+    where ppConstr (name, fs) = sep $ (text "#" <> ppr name) : map ppr fs+          cs' = map ppConstr $ M.toList cs++emptyShape :: ValueShape -> Bool+emptyShape (ShapeDim d s) = d == 0 || emptyShape s+emptyShape _ = False++typeShape :: M.Map VName (Shape d) -> TypeBase d () -> Shape d+typeShape shapes = go+  where go (Array _ _ et shape) =+          foldr ShapeDim (go (Scalar et)) $ shapeDims shape+        go (Scalar (Record fs)) =+          ShapeRecord $ M.map go fs+        go (Scalar (Sum cs)) =+          ShapeSum $ M.map (map go) cs+        go (Scalar (TypeVar _ _ (TypeName [] v) []))+          | Just shape <- M.lookup v shapes =+              shape+        go _ =+          ShapeLeaf++structTypeShape :: M.Map VName ValueShape -> StructType -> Shape (Maybe Int32)+structTypeShape shapes = fmap dim . typeShape shapes'+  where dim (ConstDim d) = Just $ fromIntegral d+        dim _ = Nothing+        shapes' = M.map (fmap $ ConstDim . fromIntegral) shapes++resolveTypeParams :: [VName] -> StructType -> StructType -> Env+resolveTypeParams names = match+  where match (Scalar (TypeVar _ _ tn _)) t+          | typeLeaf tn `elem` names =+              typeEnv $ M.singleton (typeLeaf tn) t+        match (Scalar (Record poly_fields)) (Scalar (Record fields)) =+          mconcat $ M.elems $+          M.intersectionWith match poly_fields fields+        match (Scalar (Sum poly_fields)) (Scalar (Sum fields)) =+          mconcat $ map mconcat $ M.elems $+          M.intersectionWith (zipWith match) poly_fields fields+        match (Scalar (Arrow _ _  poly_t1 poly_t2)) (Scalar (Arrow _ _ t1 t2)) =+          match poly_t1 t1 <> match poly_t2 t2+        match poly_t t+          | d1 : _ <- shapeDims (arrayShape poly_t),+            d2 : _ <- shapeDims (arrayShape t) =+              matchDims d1 d2 <> match (stripArray 1 poly_t) (stripArray 1 t)+        match _ _ = mempty++        matchDims (NamedDim (QualName _ d1)) (ConstDim d2)+          | d1 `elem` names =+              i32Env $ M.singleton d1 $ fromIntegral d2+        matchDims _ _ = mempty++resolveExistentials :: [VName] -> StructType -> ValueShape -> M.Map VName Int32+resolveExistentials names = match+  where match (Scalar (Record poly_fields)) (ShapeRecord fields) =+          mconcat $ M.elems $+          M.intersectionWith match poly_fields fields+        match (Scalar (Sum poly_fields)) (ShapeSum fields) =+          mconcat $ map mconcat $ M.elems $+          M.intersectionWith (zipWith match) poly_fields fields+        match poly_t (ShapeDim d2 rowshape)+          | d1 : _ <- shapeDims (arrayShape poly_t) =+              matchDims d1 d2 <> match (stripArray 1 poly_t) rowshape+        match _ _ = mempty++        matchDims (NamedDim (QualName _ d1)) d2+          | d1 `elem` names = M.singleton d1 d2+        matchDims _ _ = mempty++-- | A fully evaluated Futhark value.+data Value = ValuePrim !PrimValue+           | ValueArray ValueShape !(Array Int Value)+             -- Stores the full shape.+           | ValueRecord (M.Map Name Value)+           | ValueFun (Value -> EvalM Value)+           | ValueSum ValueShape Name [Value]+             -- Stores the full shape.++instance Eq Value where+  ValuePrim x == ValuePrim y = x == y+  ValueArray _ x == ValueArray _ y = x == y+  ValueRecord x == ValueRecord y = x == y+  ValueSum _ n1 vs1 == ValueSum _ n2 vs2 = n1 == n2 && vs1 == vs2+  _ == _ = False+ instance Pretty Value where   ppr (ValuePrim v)  = ppr v-  ppr (ValueArray a) =+  ppr (ValueArray _ a) =     let elements  = elems a -- [Value]         (x:_)     = elements         separator = case x of-                      (ValueArray _) -> comma <> line+                      ValueArray _ _ -> comma <> line                       _              -> comma <> space      in brackets $ cat $ punctuate separator (map ppr elements)    ppr (ValueRecord m) = prettyRecord m   ppr ValueFun{} = text "#<fun>"-  ppr (ValueSum n vs) = text "#" <> sep (ppr n : map ppr vs)---- | Create an array value; failing if that would result in an--- irregular array.-mkArray :: [Value] -> Maybe Value-mkArray vs =-  case vs of [] -> Just $ toArray' vs-             v:_ | all ((==valueShape v) . valueShape) vs -> Just $ toArray' vs-                 | otherwise -> Nothing---- | A shape is a tree to accomodate the case of records.-data Shape = ShapeDim Int32 Shape-           | ShapeLeaf-           | ShapeRecord (M.Map Name Shape)-           deriving (Eq, Show)--instance Pretty Shape where-  ppr ShapeLeaf = mempty-  ppr (ShapeDim d s) = brackets (ppr d) <> ppr s-  ppr (ShapeRecord m) = prettyRecord m--emptyShape :: Shape -> Bool-emptyShape ShapeLeaf = False-emptyShape (ShapeDim d s) = d == 0 || emptyShape s-emptyShape (ShapeRecord fs) = any emptyShape fs+  ppr (ValueSum _ n vs) = text "#" <> sep (ppr n : map ppr vs) -valueShape :: Value -> Shape-valueShape (ValueArray arr) = ShapeDim (arrayLength arr) $-                              case elems arr of-                                []  -> ShapeLeaf-                                v:_ -> valueShape v+valueShape :: Value -> ValueShape+valueShape (ValueArray shape _) = shape valueShape (ValueRecord fs) = ShapeRecord $ M.map valueShape fs+valueShape (ValueSum shape _ _) = shape valueShape _ = ShapeLeaf +checkShape :: Shape (Maybe Int32) -> ValueShape -> Maybe ValueShape+checkShape (ShapeDim Nothing shape1) (ShapeDim d2 shape2) =+  ShapeDim d2 <$> checkShape shape1 shape2+checkShape (ShapeDim (Just d1) shape1) (ShapeDim d2 shape2) = do+  guard $ d1 == d2+  ShapeDim d2 <$> checkShape shape1 shape2+checkShape (ShapeDim d1 shape1) ShapeLeaf =+  -- This case is for handling polymorphism, when a function doesn't+  -- know that the array it produced actually has more dimensions.+  ShapeDim (fromMaybe 0 d1) <$> checkShape shape1 ShapeLeaf+checkShape (ShapeRecord shapes1) (ShapeRecord shapes2) =+  ShapeRecord <$> sequence (M.intersectionWith checkShape shapes1 shapes2)+checkShape (ShapeRecord shapes1) ShapeLeaf =+  Just $ fromMaybe 0 <$> ShapeRecord shapes1+checkShape (ShapeSum shapes1) (ShapeSum shapes2) =+  ShapeSum <$> sequence (M.intersectionWith (zipWithM checkShape) shapes1 shapes2)+checkShape (ShapeSum shapes1) ShapeLeaf =+  Just $ fromMaybe 0 <$> ShapeSum shapes1+checkShape _ shape2 =+  Just shape2++-- | Does the value correspond to an empty array? isEmptyArray :: Value -> Bool isEmptyArray = emptyShape . valueShape +-- | String representation of an empty array with the provided element+-- type.  This is pretty ad-hoc - don't expect good results unless the+-- element type is a primitive.+prettyEmptyArray :: TypeBase () () -> Value -> String+prettyEmptyArray t v =+  "empty(" ++ dims (valueShape v) ++ pretty t' ++ ")"+  where t' = stripArray (arrayRank t) t+        dims (ShapeDim n rowshape) =+          "[" ++ pretty n ++ "]" ++ dims rowshape+        dims _ = ""++-- | Create an array value; failing if that would result in an+-- irregular array.+mkArray :: TypeBase Int32 () -> [Value] -> Maybe Value+mkArray t [] =+  return $ toArray (typeShape mempty t) []+mkArray _ (v:vs) = do+  let v_shape = valueShape v+  guard $ all ((==v_shape) . valueShape) vs+  return $ toArray' v_shape $ v:vs+ arrayLength :: Integral int => Array Int Value -> int arrayLength = fromIntegral . (+1) . snd . bounds @@ -205,6 +323,8 @@  -- | A TermValue with a 'Nothing' type annotation is an intrinsic. data TermBinding = TermValue (Maybe T.BoundV) Value+                 | TermPoly (Maybe T.BoundV) (StructType -> EvalM Value)+                   -- ^ A polymorphic value that must be instantiated.                  | TermModule Module  data Module = Module Env@@ -212,7 +332,7 @@  data Env = Env { envTerm :: M.Map VName TermBinding                , envType :: M.Map VName T.TypeBinding-               , envShapes :: M.Map VName Shape+               , envShapes :: M.Map VName ValueShape                  -- ^ A mapping from type parameters to the shapes of                  -- the value to which they were initially bound.                }@@ -238,6 +358,18 @@                , envShapes = mempty                } +typeEnv :: M.Map VName StructType -> Env+typeEnv m = Env { envTerm = mempty+                , envType = M.map tbind m+                , envShapes = mempty+                }+  where tbind = T.TypeAbbr Unlifted []++i32Env :: M.Map VName Int32 -> Env+i32Env = valEnv . M.map f+  where f x = (Just $ T.BoundV [] $ Scalar $ Prim $ Signed Int32,+               ValuePrim $ SignedValue $ Int32Value x)+ instance Show InterpreterError where   show (InterpreterError s) = s @@ -254,8 +386,8 @@   top <- fromMaybe noLoc . maybeHead . drop 1 <$> stacktrace   liftF $ ExtOpTrace top (pretty v) () -typeEnv :: Env -> T.Env-typeEnv env =+typeCheckerEnv :: Env -> T.Env+typeCheckerEnv env =   -- FIXME: some shadowing issues are probably not right here.   let valMap (TermValue (Just t) _) = Just t       valMap _ = Nothing@@ -276,19 +408,19 @@     Nothing -> return ()     Just stack' -> liftF $ ExtOpBreak stack' () -fromArray :: Value -> [Value]-fromArray (ValueArray as) = elems as+fromArray :: Value -> (ValueShape, [Value])+fromArray (ValueArray shape as) = (shape, elems as) fromArray v = error $ "Expected array value, but found: " ++ pretty v --- | This is where we enforce the regularity constraint for arrays.-toArray :: [Value] -> EvalM Value-toArray = maybe (bad noLoc mempty "irregular array") return . mkArray+toArray :: ValueShape -> [Value] -> Value+toArray shape vs = ValueArray shape (listArray (0, length vs - 1) vs) -toArray' :: [Value] -> Value-toArray' vs = ValueArray (listArray (0, length vs - 1) vs)+toArray' :: ValueShape -> [Value] -> Value+toArray' rowshape vs = ValueArray shape (listArray (0, length vs - 1) vs)+  where shape = ShapeDim (genericLength vs) rowshape  apply :: SrcLoc -> Env -> Value -> Value -> EvalM Value-apply loc env (ValueFun f) v = stacking loc env $ f v+apply loc env (ValueFun f) v = stacking loc env (f v) apply _ _ f _ = error $ "Cannot apply non-function: " ++ pretty f  apply2 :: SrcLoc -> Env -> Value -> Value -> Value -> EvalM Value@@ -299,110 +431,38 @@ matchPattern env p v = do   m <- runMaybeT $ patternMatch env p v   case m of-    Nothing    -> error $ "matchPattern: missing case for " ++ pretty p ++ " and " ++ pretty v+    Nothing   -> error $ "matchPattern: missing case for " ++ pretty p ++ " and " ++ pretty v     Just env' -> return env'  patternMatch :: Env -> Pattern -> Value -> MaybeT EvalM Env patternMatch env (Id v (Info t) _) val =-  lift $ pure $ valEnv (M.singleton v (Just $ T.BoundV [] $ toStruct t, val)) <> env+  lift $ pure $+  valEnv (M.singleton v (Just $ T.BoundV [] $ toStruct t, val)) <> env patternMatch env Wildcard{} _ =   lift $ pure env patternMatch env (TuplePattern ps _) (ValueRecord vs)   | length ps == length vs' =-    foldM (\env' (p,v) -> patternMatch env' p v) env $-    zip ps (map snd $ sortFields vs)+      foldM (\env' (p,v) -> patternMatch env' p v) env $+      zip ps (map snd $ sortFields vs)     where vs' = sortFields vs patternMatch env (RecordPattern ps _) (ValueRecord vs)   | length ps == length vs' =-    foldM (\env' (p,v) -> patternMatch env' p v) env $-    zip (map snd $ sortFields $ M.fromList ps) (map snd $ sortFields vs)+      foldM (\env' (p,v) -> patternMatch env' p v) env $+      zip (map snd $ sortFields $ M.fromList ps) (map snd $ sortFields vs)     where vs' = sortFields vs patternMatch env (PatternParens p _) v = patternMatch env p v-patternMatch env (PatternAscription p td loc) v = do-  let t = evalType env $ unInfo $ expandedType td-  case matchValueToType env t v of-    Left err -> lift $ bad loc env err-    Right env' -> patternMatch env' p v+patternMatch env (PatternAscription p _ _) v =+  patternMatch env p v patternMatch env (PatternLit e _ _) v = do   v' <- lift $ eval env e   if v == v'     then pure env     else mzero-patternMatch env (PatternConstr n _ ps _) (ValueSum n' vs)+patternMatch env (PatternConstr n _ ps _) (ValueSum _ n' vs)   | n == n' =-    foldM (\env' (p,v) -> patternMatch env' p v) env $ zip ps vs+      foldM (\env' (p,v) -> patternMatch env' p v) env $ zip ps vs patternMatch _ _ _ = mzero --- | For matching size annotations (the actual type will have been--- verified by the type checker).  It is assumed that previously--- unbound names are in binding position here.-matchValueToType :: Env-                 -> StructType-                 -> Value-                 -> Either String Env--matchValueToType env t@(Scalar (TypeVar _ _ tn [])) val-  | Just shape <- M.lookup (typeLeaf tn) $ envShapes env,-    shape /= valueShape val =-      Left $ "Value passed for type parameter " <> quote (prettyName (typeLeaf tn)) <>-      " does not match shape " <> pretty shape <>-      " of previously observed value."-  | Nothing <- M.lookup (typeLeaf tn) $ envShapes env =-      matchValueToType (tnenv <> env) t val-      where tnenv = Env mempty mempty $ M.singleton (typeLeaf tn) (valueShape val)--matchValueToType env t@(Array _ _ _ (ShapeDecl ds@(d:_))) val@(ValueArray arr) =-  case d of-    NamedDim v-      | Just x <- look v ->-          if x == arr_n-          then continue env-          else emptyOrWrong $ quote (pretty v) <> " (" <> pretty x <> ")"-      | otherwise ->-          continue $-          valEnv (M.singleton (qualLeaf v)-                   (Just $ T.BoundV [] $ Scalar $ Prim $ Signed Int32,-                    ValuePrim $ SignedValue $ Int32Value arr_n))-          <> env-    AnyDim -> continue env-    ConstDim x-      | fromIntegral x == arr_n -> continue env-      | otherwise -> emptyOrWrong $ pretty x-  where arr_n = arrayLength arr--        look v-          | Just (TermValue _ (ValuePrim (SignedValue (Int32Value x)))) <--              lookupVar v env = Just x-          | otherwise = Nothing--        continue env' = case elems arr of-          [] -> return env'-          v:_ -> matchValueToType env' (stripArray 1 t) v--        -- Empty arrays always match if nothing else does.-        emptyOrWrong x-          | any zeroDim ds, emptyShape (valueShape val) =-              Right env-          | otherwise = wrong x--        wrong x = Left $ "Size annotation " <> x <>-                  " does not match observed size " <> pretty arr_n <> "."--        zeroDim (NamedDim v) = isNothing (look v) || Just 0 == look v-        zeroDim AnyDim = True-        zeroDim (ConstDim x) = x == 0--matchValueToType env (Scalar (Record fs)) (ValueRecord arr) =-  foldM (\env' (t, v) -> matchValueToType env' t v) env $-  M.intersectionWith (,) fs arr--matchValueToType env _ _ = return env--bindToZero :: [VName] -> Env-bindToZero = valEnv . M.fromList . map f-  where f v = (v, (Just $ T.BoundV [] $ Scalar $ Prim $ Signed Int32,-                   ValuePrim $ SignedValue $ Int32Value 0))- data Indexing = IndexingFix Int32               | IndexingSlice (Maybe Int32) (Maybe Int32) (Maybe Int32) @@ -420,8 +480,8 @@     maybe mempty ppr i <> text ":"  indexesFor :: Maybe Int32 -> Maybe Int32 -> Maybe Int32-           -> Array Int Value -> Maybe [Int]-indexesFor start end stride arr+           -> Int32 -> Maybe [Int]+indexesFor start end stride n   | (start', end', stride') <- slice,     end' == start' || signum' (end' - start') == signum' stride',     stride' /= 0,@@ -430,9 +490,7 @@       Just $ map fromIntegral is   | otherwise =       Nothing-  where n = arrayLength arr--        inBounds i = i >= 0 && i < n+  where inBounds i = i >= 0 && i < n          slice =           case (start, end, stride) of@@ -454,34 +512,43 @@ signum' 0 = 1 signum' x = signum x +indexShape :: [Indexing] -> ValueShape -> ValueShape+indexShape (IndexingFix{}:is) (ShapeDim _ shape) =+  indexShape is shape+indexShape (IndexingSlice start end stride:is) (ShapeDim d shape) =+  ShapeDim n $ indexShape is shape+  where n = maybe 0 genericLength $ indexesFor start end stride d+indexShape _ shape =+  shape+ indexArray :: [Indexing] -> Value -> Maybe Value-indexArray (IndexingFix i:is) (ValueArray arr)+indexArray (IndexingFix i:is) (ValueArray _ arr)   | i >= 0, i < n =       indexArray is $ arr ! fromIntegral i   | otherwise =       Nothing   where n = arrayLength arr-indexArray (IndexingSlice start end stride:is) (ValueArray arr) = do-  js <- indexesFor start end stride arr-  toArray' <$> mapM (indexArray is . (arr!)) js+indexArray (IndexingSlice start end stride:is) (ValueArray (ShapeDim _ rowshape) arr) = do+  js <- indexesFor start end stride $ arrayLength arr+  toArray' (indexShape is rowshape) <$> mapM (indexArray is . (arr!)) js indexArray _ v = Just v  updateArray :: [Indexing] -> Value -> Value -> Maybe Value-updateArray (IndexingFix i:is) (ValueArray arr) v+updateArray (IndexingFix i:is) (ValueArray shape arr) v   | i >= 0, i < n = do       v' <- updateArray is (arr ! i') v-      Just $ ValueArray $ arr // [(i', v')]+      Just $ ValueArray shape $ arr // [(i', v')]   | otherwise =       Nothing   where n = arrayLength arr         i' = fromIntegral i-updateArray (IndexingSlice start end stride:is) (ValueArray arr) (ValueArray v) = do-  arr_is <- indexesFor start end stride arr+updateArray (IndexingSlice start end stride:is) (ValueArray shape arr) (ValueArray _ v) = do+  arr_is <- indexesFor start end stride $ arrayLength arr   guard $ length arr_is == arrayLength v   let update arr' (i, v') = do         x <- updateArray is (arr!i) v'         return $ arr' // [(i, x)]-  fmap ValueArray $ foldM update arr $ zip arr_is $ elems v+  fmap (ValueArray shape) $ foldM update arr $ zip arr_is $ elems v updateArray _ _ v = Just v  evalDimIndex :: Env -> DimIndex -> EvalM Indexing@@ -499,12 +566,6 @@             pretty (valueShape arr) <> "."   maybe oob return $ indexArray is arr -evalTermVar :: Env -> QualName VName -> EvalM Value-evalTermVar env qv =-  case lookupVar qv env of-    Just (TermValue _ v) -> return v-    _ -> error $ quote (pretty qv) <> " is not bound to a value."- -- | Expand type based on information that was not available at -- type-checking time (the structure of abstract types). evalType :: Env -> StructType -> StructType@@ -542,40 +603,91 @@         matchPtoA _ _ = mempty evalType env (Scalar (Sum cs)) = Scalar $ Sum $ (fmap . fmap) (evalType env) cs -evalFunction :: Env -> [TypeParam] -> [Pattern] -> Exp-             -> (Aliasing, StructType) -> SrcLoc -> EvalM Value+evalTermVar :: Env -> QualName VName -> StructType -> EvalM Value+evalTermVar env qv t =+  case lookupVar qv env of+    Just (TermPoly _ v) -> do size_env <- extSizeEnv+                              v $ evalType (size_env <> env) t+    Just (TermValue _ v) -> return v+    _ -> error $ "`" <> pretty qv <> "` is not bound to a value." +typeValueShape :: Env -> StructType -> EvalM ValueShape+typeValueShape env t = do+  size_env <- extSizeEnv+  let t' = evalType (size_env <> env) t+  case traverse dim $ typeShape mempty t' of+    Nothing -> error $ "typeValueShape: failed to fully evaluate type " ++ pretty t'+    Just shape -> return shape+  where dim (ConstDim x) = Just $ fromIntegral x+        dim _ = Nothing++evalFunction :: Env -> [VName] -> [Pattern] -> Exp -> StructType -> EvalM Value+ -- We treat zero-parameter lambdas as simply an expression to -- evaluate immediately.  Note that this is *not* the same as a lambda -- that takes an empty tuple '()' as argument!  Zero-parameter lambdas -- can never occur in a well-formed Futhark program, but they are -- convenient in the interpreter.-evalFunction env tparams [] body (_, t) loc = do-  -- All remaining size parameters that have not yet been assigned a-  -- value (because they were inner dimensions of empty arrays) are-  -- now assigned a zero.-  let unbound_dims = bindToZero $ map typeParamName $ filter isDimParam tparams-  v <- eval (env <> unbound_dims) body-  case (t, v) of-    (Scalar (Arrow _ _ _ rt), ValueFun f) ->-      return $ ValueFun $ \arg -> do r <- f arg-                                     match (evalType env rt) r-    _ -> match t v-  where match vt v =-          case matchValueToType env vt v of-            Right _ -> return v-            Left err ->-              bad loc env $ "Value " <> quote (pretty v) <>-              " cannot match type " <> quote (pretty vt) <> ": " ++ err--        isDimParam TypeParamDim{} = True-        isDimParam _ = False+evalFunction env _ [] body rettype =+  -- Eta-expand the rest to make any sizes visible.+  etaExpand [] env rettype+  where etaExpand vs env' (Scalar (Arrow _ _ pt rt)) =+          return $ ValueFun $ \v -> do+          env'' <- matchPattern env' (Wildcard (Info $ fromStruct pt) noLoc) v+          etaExpand (v:vs) env'' rt+        etaExpand vs env' _ = do+          f <- eval env' body+          foldM (apply noLoc mempty) f $ reverse vs -evalFunction env tparams (p:ps) body (als, ret) loc =+evalFunction env missing_sizes (p:ps) body rettype =   return $ ValueFun $ \v -> do     env' <- matchPattern env p v-    evalFunction env' tparams ps body (als, ret) loc+    -- Fix up the last sizes, if any.+    let env'' | null missing_sizes = env'+              | otherwise = env' <>+                            i32Env (resolveExistentials missing_sizes+                                    (patternStructType p) (valueShape v))+    evalFunction env'' missing_sizes ps body rettype +evalFunctionBinding :: Env+                    -> [TypeParam] -> [Pattern] -> StructType -> [VName] -> Exp+                    -> EvalM TermBinding+evalFunctionBinding env tparams ps ret retext fbody = do+  let ret' = evalType env ret+      arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt+      ftype = foldr (arrow . patternParam) ret' ps++  -- Distinguish polymorphic and non-polymorphic bindings here.+  if null tparams+  then TermValue (Just $ T.BoundV [] ftype) <$>+       (returned env ret retext =<< evalFunction env [] ps fbody ret')+  else return $ TermPoly (Just $ T.BoundV [] ftype) $ \ftype' -> do+         let tparam_names = map typeParamName tparams+             env' = resolveTypeParams tparam_names ftype ftype' <> env++             -- In some cases (abstract lifted types) there may be+             -- missing sizes that were not fixed by the type+             -- instantiation.  These will have to be set by looking+             -- at the actual function arguments.+             missing_sizes =+               filter (`M.notMember` envTerm env') $+               map typeParamName (filter isSizeParam tparams)+         returned env ret retext =<< evalFunction env' missing_sizes ps fbody ret'++evalArg :: Env -> Exp -> Maybe VName -> EvalM Value+evalArg env e ext = do+  v <- eval env e+  case ext of Just ext' -> putExtSize ext' $ asInt32 v+              Nothing -> return ()+  return v++returned :: Env -> TypeBase (DimDecl VName) als -> [VName] -> Value -> EvalM Value+returned _ _ [] v = return v+returned env ret retext v = do+  mapM_ (uncurry putExtSize) $ M.toList $+    resolveExistentials retext (evalType env $ toStruct ret) $ valueShape v+  return v+ eval :: Env -> Exp -> EvalM Value  eval _ (Literal v _) = return $ ValuePrim v@@ -595,12 +707,20 @@           v <- eval env $ Var (qualName k) t loc           return (baseName k, v) -eval env (ArrayLit vs _ _) = toArray =<< mapM (eval env) vs- eval _ (StringLit vs _) =-  toArray $ map (ValuePrim . UnsignedValue . Int8Value . fromIntegral) vs+  return $ toArray' ShapeLeaf $+  map (ValuePrim . UnsignedValue . Int8Value . fromIntegral) vs -eval env (Range start maybe_second end (Info t) loc) = do+eval env (ArrayLit [] (Info t) _) = do+  t' <- typeValueShape env $ toStruct t+  return $ toArray t' []++eval env (ArrayLit (v:vs) _ _) = do+  v' <- eval env v+  vs' <- mapM (eval env) vs+  return $ toArray' (valueShape v') (v':vs')++eval env (Range start maybe_second end (Info t, Info retext) loc) = do   start' <- asInteger <$> eval env start   maybe_second' <- traverse (fmap asInteger . eval env) maybe_second   end' <- traverse (fmap asInteger . eval env) end@@ -626,7 +746,8 @@             (x-1, second' - start', start' <= x && second' > start')    if ok-    then toArray $ map toInt [start',start'+step..end_adj]+    then returned env t retext $+         toArray' ShapeLeaf $ map toInt [start',start'+step..end_adj]     else bad loc env $ badRange start' maybe_second' end'    where toInt =@@ -648,26 +769,28 @@              UpToExclusive x -> "..<"++ pretty x) ++           " is invalid." -eval env (Var qv _ _) = evalTermVar env qv+eval env (Var qv (Info t) _) = evalTermVar env qv (toStruct t) -eval env (Ascript e td _ loc) = do-  v <- eval env e+eval env (Ascript e _ _ ) = eval env e++eval env (Coerce e td (Info ret, Info retext) loc) = do+  v <- returned env ret retext =<< eval env e   let t = evalType env $ unInfo $ expandedType td-  case matchValueToType env t v of-    Right _ -> return v-    Left err -> bad loc env $ "Value " <> quote (pretty v) <> " cannot match shape of type " <>-                quote (pretty (declaredType td)) <> " (" <> quote (pretty t) <> "): " ++ err+  case checkShape (structTypeShape (envShapes env) t) (valueShape v) of+    Just _ -> return v+    Nothing ->+      bad loc env $ "Value `" <> pretty v <> "` of shape `" ++ pretty (valueShape v) +++      "` cannot match shape of type `" <>+      pretty (declaredType td) <> "` (`" <> pretty t <> "`)" -eval env (LetPat p e body _ _) = do+eval env (LetPat p e body (Info ret, Info retext) _) = do   v <- eval env e   env' <- matchPattern env p v-  eval env' body+  returned env ret retext =<< eval env' body -eval env (LetFun f (tparams, pats, _, Info ret, fbody) body loc) = do-  v <- evalFunction env tparams pats fbody (mempty, ret) loc-  let arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt-      ftype = T.BoundV [] $ foldr (arrow . patternParam) ret pats-  eval (valEnv (M.singleton f (Just ftype, v)) <> env) body+eval env (LetFun f (tparams, ps, _, Info ret, fbody) body _ _) = do+  binding <- evalFunctionBinding env tparams ps ret [] fbody+  eval (env { envTerm = M.insert f binding $ envTerm env }) body  eval _ (IntLit v (Info t) _) =   case t of@@ -685,7 +808,9 @@       return $ ValuePrim $ FloatValue $ floatValue ft v     _ -> error $ "eval: nonsensical type for float literal: " ++ pretty t -eval env (BinOp (op, _) op_t (x, _) (y, _) _ loc)+eval env (BinOp (op, _) op_t+          (x, Info (_, xext)) (y, Info (_, yext))+          (Info t) (Info retext) loc)   | baseString (qualLeaf op) == "&&" = do       x' <- asBool <$> eval env x       if x'@@ -698,18 +823,21 @@         else eval env y   | otherwise = do       op' <- eval env $ Var op op_t loc-      x' <- eval env x-      y' <- eval env y-      apply2 loc env op' x' y'+      x' <- evalArg env x xext+      y' <- evalArg env y yext+      returned env t retext =<< apply2 loc env op' x' y' -eval env (If cond e1 e2 _ _) = do+eval env (If cond e1 e2 (Info ret, Info retext) _) = do   cond' <- asBool <$> eval env cond-  if cond' then eval env e1 else eval env e2+  returned env ret retext =<<+    if cond' then eval env e1 else eval env e2 -eval env (Apply f x _ _ loc) = do+eval env (Apply f x (Info (_, ext)) (Info t, Info retext) loc) = do+  -- It is important that 'x' is evaluated first in order to bring any+  -- sizes into scope that may be used in the type of 'f'.+  x' <- evalArg env x ext   f' <- eval env f-  x' <- eval env x-  apply loc env f' x'+  returned env t retext =<< apply loc env f' x'  eval env (Negate e _) = do   ev <- eval env e@@ -724,12 +852,12 @@     ValuePrim (UnsignedValue (Int64Value v)) -> return $ UnsignedValue $ Int64Value (-v)     ValuePrim (FloatValue (Float32Value v)) -> return $ FloatValue $ Float32Value (-v)     ValuePrim (FloatValue (Float64Value v)) -> return $ FloatValue $ Float64Value (-v)-    _ -> fail $ "Cannot negate " ++ pretty ev+    _ -> error $ "Cannot negate " ++ pretty ev -eval env (Index e is _ loc) = do+eval env (Index e is (Info t, Info retext) loc) = do   is' <- mapM (evalDimIndex env) is   arr <- eval env e-  evalIndex loc env is' arr+  returned env t retext =<< evalIndex loc env is' arr  eval env (Update src is v loc) =   maybe oob return =<<@@ -745,9 +873,10 @@         update _ _ _ = error "eval RecordUpdate: invalid value."  eval env (LetWith dest src is v body _ loc) = do+  let Ident src_vn (Info src_t) _ = src   dest' <- maybe oob return =<<     updateArray <$> mapM (evalDimIndex env) is <*>-    evalTermVar env (qualName $ identName src) <*> eval env v+    evalTermVar env (qualName src_vn) (toStruct src_t) <*> eval env v   let t = T.BoundV [] $ toStruct $ unInfo $ identType dest   eval (valEnv (M.singleton (identName dest) (Just t, dest')) <> env) body   where oob = bad loc env "Bad update"@@ -757,39 +886,48 @@ -- that takes an empty tuple '()' as argument!  Zero-parameter lambdas -- can never occur in a well-formed Futhark program, but they are -- convenient in the interpreter.-eval env (Lambda ps body _ (Info (als, ret)) loc) =-  evalFunction env [] ps body (als, ret) loc+eval env (Lambda ps body _ (Info (_, rt)) _) =+  evalFunction env [] ps body rt -eval env (OpSection qv _  _) = evalTermVar env qv+eval env (OpSection qv (Info t) _) = evalTermVar env qv $ toStruct t -eval env (OpSectionLeft qv _ e _ _ loc) =-  join $ apply loc env <$> evalTermVar env qv <*> eval env e+eval env (OpSectionLeft qv _ e (Info (_, argext), _) (Info t, Info retext) loc) = do+  v <- evalArg env e argext+  f <- evalTermVar env qv (toStruct t)+  returned env t retext =<< apply loc env f v -eval env (OpSectionRight qv _ e _ _ loc) = do-  f <- evalTermVar env qv-  y <- eval env e-  return $ ValueFun $ \x -> join $ apply loc env <$> apply loc env f x <*> pure y+eval env (OpSectionRight qv _ e (Info _, Info (_, argext)) (Info t) loc) = do+  y <- evalArg env e argext+  return $ ValueFun $ \x -> do+    f <- evalTermVar env qv $ toStruct t+    apply2 loc env f x y  eval env (IndexSection is _ loc) = do   is' <- mapM (evalDimIndex env) is   return $ ValueFun $ evalIndex loc env is' -eval _ (ProjectSection ks _ _) = return $ ValueFun $ flip (foldM walk) ks+eval _ (ProjectSection ks _ _) =+  return $ ValueFun $ flip (foldM walk) ks   where walk (ValueRecord fs) f           | Just v' <- M.lookup f fs = return v'-        walk _ _ = fail "Value does not have expected field."+        walk _ _ = error "Value does not have expected field." -eval env (DoLoop pat init_e form body _) = do+eval env (DoLoop sparams pat init_e form body (Info (ret, retext)) _) = do   init_v <- eval env init_e-  case form of For iv bound -> do-                 bound' <- asSigned <$> eval env bound-                 forLoop (identName iv) bound' (zero bound') init_v-               ForIn in_pat in_e -> do-                 in_vs <- fromArray <$> eval env in_e-                 foldM (forInLoop in_pat) init_v in_vs-               While cond ->-                 whileLoop cond init_v-  where withLoopParams = matchPattern env pat+  returned env ret retext =<<+    case form of For iv bound -> do+                   bound' <- asSigned <$> eval env bound+                   forLoop (identName iv) bound' (zero bound') init_v+                 ForIn in_pat in_e -> do+                   (_, in_vs) <- fromArray <$> eval env in_e+                   foldM (forInLoop in_pat) init_v in_vs+                 While cond ->+                   whileLoop cond init_v+  where withLoopParams v =+          let sparams' =+                resolveExistentials sparams+                (patternStructType pat) (valueShape v)+          in matchPattern (i32Env sparams' <> env) pat v          inc = (`P.doAdd` Int64Value 1)         zero = (`P.doMul` Int64Value 0)@@ -818,7 +956,7 @@   v <- eval env e   case v of     ValueRecord fs | Just v' <- M.lookup f fs -> return v'-    _ -> fail "Value does not have expected field."+    _ -> error "Value does not have expected field."  eval env (Unsafe e _) = eval env e @@ -827,15 +965,16 @@   unless cond $ bad loc env s   eval env e -eval env (Constr c es _ _) = do+eval env (Constr c es (Info t) _) = do   vs <- mapM (eval env) es-  return $ ValueSum c vs+  shape <- typeValueShape env $ toStruct t+  return $ ValueSum shape c vs -eval env (Match e cs _ _) = do+eval env (Match e cs (Info ret, Info retext) _) = do   v <- eval env e-  match v $ NE.toList cs+  returned env ret retext =<< match v (NE.toList cs)   where match _ [] =-          fail "Pattern match failure."+          error "Pattern match failure."         match v (c:cs') = do           c' <- evalCase v env c           case c' of@@ -862,6 +1001,7 @@     onModule (ModuleFun f) =       ModuleFun $ \m -> onModule <$> f (substituteInModule rev_substs m)     onTerm (TermValue t v) = TermValue t v+    onTerm (TermPoly t v) = TermPoly t v     onTerm (TermModule m) = TermModule $ onModule m     onType (T.TypeAbbr l ps t) = T.TypeAbbr l ps $ first onDim t     onDim (NamedDim v) = NamedDim $ replaceQ v@@ -907,22 +1047,24 @@     Just (se, rsubsts) -> ModAscript e se rsubsts loc  evalModExp env (ModApply f e (Info psubst) (Info rsubst) _) = do-  ModuleFun f' <- evalModExp env f-  e' <- evalModExp env e-  substituteInModule rsubst <$> f' (substituteInModule psubst e')+  f' <- evalModExp env f+  case f' of+    ModuleFun f'' -> do+      e' <- evalModExp env e+      substituteInModule rsubst <$> f'' (substituteInModule psubst e')+    _ -> error "Expected ModuleFun."  evalDec :: Env -> Dec -> EvalM Env -evalDec env (ValDec (ValBind _ v _ (Info t) tps ps def _ loc)) = do-  let t' = evalType env t-      arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt-      ftype = T.BoundV [] $ foldr (arrow . patternParam) t' ps-  val <- evalFunction env tps ps def (mempty, t') loc-  return $ valEnv (M.singleton v (Just ftype, val)) <> env+evalDec env (ValDec (ValBind _ v _ (Info (ret, retext)) tparams ps fbody _ _)) = do+  binding <- evalFunctionBinding env tparams ps ret retext fbody+  return $ env { envTerm = M.insert v binding $ envTerm env }  evalDec env (OpenDec me _) = do-  Module me' <- evalModExp env me-  return $ me' <> env+  me' <- evalModExp env me+  case me' of+    Module me'' -> return $ me'' <> env+    _ -> error "Expected Module"  evalDec env (ImportDec name name' loc) =   evalDec env $ LocalDec (OpenDec (ModImport name name' loc) loc) loc@@ -1015,7 +1157,8 @@     fun1 f =       TermValue Nothing $ ValueFun $ \x -> f x     fun2 f =-      TermValue Nothing $ ValueFun $ \x -> return $ ValueFun $ \y -> f x y+      TermValue Nothing $ ValueFun $ \x ->+      return $ ValueFun $ \y -> f x y     fun2t f =       TermValue Nothing $ ValueFun $ \v ->       case fromTuple v of Just [x,y] -> f x y@@ -1090,8 +1233,10 @@     def ">>" = Just $ bopDef $ sintOp P.AShr ++ uintOp P.LShr     def "<<" = Just $ bopDef $ intOp P.Shl     def ">>>" = Just $ bopDef $ sintOp P.LShr ++ uintOp P.LShr-    def "==" = Just $ fun2 $ \xs ys -> return $ ValuePrim $ BoolValue $ xs == ys-    def "!=" = Just $ fun2 $ \xs ys -> return $ ValuePrim $ BoolValue $ xs /= ys+    def "==" = Just $ fun2 $+               \xs ys -> return $ ValuePrim $ BoolValue $ xs == ys+    def "!=" = Just $ fun2 $+               \xs ys -> return $ ValuePrim $ BoolValue $ xs /= ys      -- The short-circuiting is handled directly in 'eval'; these cases     -- are only used when partially applying and such.@@ -1152,23 +1297,33 @@     def s | "reduce_stream" `isPrefixOf` s =               Just $ fun3t $ \_ f arg -> stream f arg -    def "map" = Just $ fun2t $ \f xs ->-      toArray =<< mapM (apply noLoc mempty f) (fromArray xs)+    def "map" = Just $ TermPoly Nothing $ \t -> return $ ValueFun $ \v ->+      case (fromTuple v, unfoldFunType t) of+        (Just [f, xs], ([_], ret_t))+          | Just rowshape <- typeRowShape ret_t ->+              toArray' rowshape <$> mapM (apply noLoc mempty f) (snd $ fromArray xs)+          | otherwise ->+              error $ "Bad return type: " ++ pretty ret_t+        _ ->+          error $ "Invalid arguments to map intrinsic:\n" +++          unlines [pretty t, pretty v]+      where typeRowShape = traverse id . structTypeShape mempty . stripArray 1      def s | "reduce" `isPrefixOf` s = Just $ fun3t $ \f ne xs ->-      foldM (apply2 noLoc mempty f) ne $ fromArray xs+      foldM (apply2 noLoc mempty f) ne $ snd $ fromArray xs      def "scan" = Just $ fun3t $ \f ne xs -> do       let next (out, acc) x = do             x' <- apply2 noLoc mempty f acc x             return (x':out, x')-      toArray . reverse . fst =<< foldM next ([], ne) (fromArray xs)+      toArray' (valueShape ne) . reverse . fst <$>+        foldM next ([], ne) (snd $ fromArray xs)      def "scatter" = Just $ fun3t $ \arr is vs ->       case arr of-        ValueArray arr' ->-          return $ ValueArray $ foldl' update arr'-          (zip (map asInt $ fromArray is) (fromArray vs))+        ValueArray shape arr' ->+          return $ ValueArray shape $ foldl' update arr' $+          zip (map asInt $ snd $ fromArray is) (snd $ fromArray vs)         _ ->           error $ "scatter expects array, but got: " ++ pretty arr       where update arr' (i, v) =@@ -1177,9 +1332,9 @@      def "hist" = Just $ fun6t $ \_ arr fun _ is vs ->       case arr of-        ValueArray arr' ->-          ValueArray <$> foldM (update fun) arr'-          (zip (map asInt $ fromArray is) (fromArray vs))+        ValueArray shape arr' ->+          ValueArray shape <$> foldM (update fun) arr'+          (zip (map asInt $ snd $ fromArray is) (snd $ fromArray vs))         _ ->           error $ "hist expects array, but got: " ++ pretty arr       where update fun arr' (i, v) =@@ -1189,16 +1344,19 @@                 return $ arr' // [(i, v')]               else return arr' -    def "partition" = Just $ fun3t $ \k f xs ->-      let next outs x = do+    def "partition" = Just $ fun3t $ \k f xs -> do+      let (ShapeDim _ rowshape, xs') = fromArray xs++          next outs x = do             i <- asInt <$> apply noLoc mempty f x             return $ insertAt i x outs           pack parts =-            toTuple [toArray' $ concat parts,-                     toArray' $+            toTuple [toArray' rowshape $ concat parts,+                     toArray' rowshape $                      map (ValuePrim . SignedValue . Int32Value . genericLength) parts]-      in pack . map reverse <$>-         foldM next (replicate (asInt k) []) (fromArray xs)++      pack . map reverse <$>+        foldM next (replicate (asInt k) []) xs'       where insertAt 0 x (l:ls) = (x:l):ls             insertAt i x (l:ls) = l:insertAt (i-1) x ls             insertAt _ _ ls = ls@@ -1206,36 +1364,50 @@     def "cmp_threshold" = Just $ fun2t $ \_ _ ->       return $ ValuePrim $ BoolValue True -    def "unzip" = Just $ fun1 $ \x ->-      toTuple <$> listPair (unzip $ map (fromPair . fromTuple) $ fromArray x)+    def "unzip" = Just $ fun1 $ \x -> do+      let ShapeDim _ (ShapeRecord fs) = valueShape x+          Just [xs_shape, ys_shape] = areTupleFields fs+          listPair (xs, ys) =+            [toArray' xs_shape xs, toArray' ys_shape ys]++      return $ toTuple $ listPair $ unzip $ map (fromPair . fromTuple) $ snd $ fromArray x       where fromPair (Just [x,y]) = (x,y)             fromPair l = error $ "Not a pair: " ++ pretty l-            listPair (xs, ys) = do-              xs' <- toArray xs-              ys' <- toArray ys-              return [xs', ys'] -    def "zip" = Just $ fun2t $ \xs ys ->-      toArray $ map toTuple $ transpose [fromArray xs, fromArray ys]+    def "zip" = Just $ fun2t $ \xs ys -> do+      let ShapeDim _ xs_rowshape = valueShape xs+          ShapeDim _ ys_rowshape = valueShape ys+      return $ toArray' (ShapeRecord (tupleFields [xs_rowshape, ys_rowshape])) $+        map toTuple $ transpose [snd $ fromArray xs, snd $ fromArray ys] -    def "concat" = Just $ fun2t $ \xs ys ->-      toArray $ fromArray xs ++ fromArray ys+    def "concat" = Just $ fun2t $ \xs ys -> do+      let (ShapeDim _ rowshape, xs') = fromArray xs+          (_, ys') = fromArray ys+      return $ toArray' rowshape $ xs' ++ ys' -    def "transpose" = Just $ fun1 $-      (toArray <=< mapM toArray) . transpose . map fromArray . fromArray+    def "transpose" = Just $ fun1 $ \xs -> do+      let (ShapeDim n (ShapeDim m shape), xs') = fromArray xs+      return $ toArray (ShapeDim m (ShapeDim n shape)) $+        map (toArray (ShapeDim n shape)) $ transpose $ map (snd . fromArray) xs' -    def "rotate" = Just $ fun2t $ \i xs ->-      if asInt i > 0-      then let (bef, aft) = splitAt (asInt i) $ fromArray xs-           in toArray $ aft ++ bef-      else let (bef, aft) = splitFromEnd (-asInt i) $ fromArray xs-           in toArray $ aft ++ bef+    def "rotate" = Just $ fun2t $ \i xs -> do+      let (shape, xs') = fromArray xs+      return $+        if asInt i > 0+        then let (bef, aft) = splitAt (asInt i) xs'+             in toArray shape $ aft ++ bef+        else let (bef, aft) = splitFromEnd (-asInt i) xs'+             in toArray shape $ aft ++ bef -    def "flatten" = Just $ fun1 $-      toArray . concatMap fromArray . fromArray+    def "flatten" = Just $ fun1 $ \xs -> do+      let (ShapeDim n (ShapeDim m shape), xs') = fromArray xs+      return $ toArray (ShapeDim (n*m) shape) $ concatMap (snd . fromArray) xs' -    def "unflatten" = Just $ fun3t $ \_ m xs ->-      toArray =<< mapM toArray (chunk (asInt m) $ fromArray xs)+    def "unflatten" = Just $ fun3t $ \n m xs -> do+      let (ShapeDim _ innershape, xs') = fromArray xs+          rowshape = ShapeDim (asInt32 m) innershape+          shape = ShapeDim (asInt32 n) rowshape+      return $ toArray shape $ map (toArray rowshape) $ chunk (asInt m) xs'      def "opaque" = Just $ fun1 return @@ -1253,7 +1425,7 @@       t <- nameFromString s `M.lookup` namesToPrimTypes       return $ T.TypeAbbr Unlifted [] $ Scalar $ Prim t -    stream f arg@(ValueArray xs) =+    stream f arg@(ValueArray _ xs) =       let n = ValuePrim $ SignedValue $ Int32Value $ arrayLength xs       in apply2 noLoc mempty f n arg     stream _ arg = error $ "Cannot stream: " ++ pretty arg@@ -1276,13 +1448,25 @@ -- horribly if these are ill-typed. interpretFunction :: Ctx -> VName -> [F.Value] -> Either String (F ExtOp Value) interpretFunction ctx fname vs = do+  ft <- case lookupVar (qualName fname) $ ctxEnv ctx of+          Just (TermValue (Just (T.BoundV _ t)) _) ->+            Right $ updateType (map valueType vs) t+          Just (TermPoly (Just (T.BoundV _ t)) _) ->+            Right $ updateType (map valueType vs) t+          _ ->+            Left $ "Unknown function `" <> prettyName fname <> "`."+   vs' <- case mapM convertValue vs of            Just vs' -> Right vs'            Nothing -> Left "Invalid input: irregular array."    Right $ runEvalM (ctxImports ctx) $ do-    f <- evalTermVar (ctxEnv ctx) (qualName fname)+    f <- evalTermVar (ctxEnv ctx) (qualName fname) ft     foldM (apply noLoc mempty) f vs' -  where convertValue (F.PrimValue p) = Just $ ValuePrim p-        convertValue (F.ArrayValue arr _) = mkArray =<< mapM convertValue (elems arr)+  where updateType (vt:vts) (Scalar (Arrow als u _ rt)) =+          Scalar $ Arrow als u (valueStructType vt) $ updateType vts rt+        updateType _ t = t++        convertValue (F.PrimValue p) = Just $ ValuePrim p+        convertValue (F.ArrayValue arr t) = mkArray t =<< mapM convertValue (elems arr)
src/Language/Futhark/Parser/Parser.y view
@@ -29,6 +29,7 @@ import Data.Char (ord) import Data.Maybe (fromMaybe, fromJust) import Data.Loc hiding (L) -- Lexer has replacements.+import Data.List (genericLength) import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as M import Data.Monoid@@ -527,19 +528,19 @@ -- permit inside array indices operations (there is an ambiguity with -- array slices). Exp :: { UncheckedExp }-     : Exp ':' TypeExpDecl { Ascript $1 $3 NoInfo (srcspan $1 $>) }-     | Exp ':>' TypeExpDecl { Ascript $1 $3 NoInfo (srcspan $1 $>) }+     : Exp ':' TypeExpDecl { Ascript $1 $3 (srcspan $1 $>) }+     | Exp ':>' TypeExpDecl { Coerce $1 $3 (NoInfo,NoInfo) (srcspan $1 $>) }      | Exp2 %prec ':'      { $1 }  Exp2 :: { UncheckedExp }      : if Exp then Exp else Exp %prec ifprec-                      { If $2 $4 $6 NoInfo (srcspan $1 $>) }+                      { If $2 $4 $6 (NoInfo,NoInfo) (srcspan $1 $>) }       | loop Pattern LoopForm do Exp %prec ifprec-         {% fmap (\t -> DoLoop $2 t $3 $5 (srcspan $1 $>)) (patternExp $2) }+         {% fmap (\t -> DoLoop [] $2 t $3 $5 NoInfo (srcspan $1 $>)) (patternExp $2) }       | loop Pattern '=' Exp LoopForm do Exp %prec ifprec-         { DoLoop $2 $4 $5 $7 (srcspan $1 $>) }+         { DoLoop [] $2 $4 $5 $7 NoInfo (srcspan $1 $>) }       | LetExp %prec letprec { $1 } @@ -577,14 +578,14 @@      | Exp2 '<|...' Exp2   { binOp $1 $2 $3 }       | Exp2 '<' Exp2              { binOp $1 (L $2 (SYMBOL Less [] (nameFromString "<"))) $3 }-     | Exp2 '`' QualName '`' Exp2 { BinOp $3 NoInfo ($1, NoInfo) ($5, NoInfo) NoInfo (srcspan $1 $>) }+     | Exp2 '`' QualName '`' Exp2 { BinOp $3 NoInfo ($1, NoInfo) ($5, NoInfo) NoInfo NoInfo (srcspan $1 $>) } -     | Exp2 '...' Exp2           { Range $1 Nothing (ToInclusive $3) NoInfo (srcspan $1 $>) }-     | Exp2 '..<' Exp2           { Range $1 Nothing (UpToExclusive $3) NoInfo (srcspan $1 $>) }-     | Exp2 '..>' Exp2           { Range $1 Nothing (DownToExclusive $3) NoInfo (srcspan $1 $>) }-     | Exp2 '..' Exp2 '...' Exp2 { Range $1 (Just $3) (ToInclusive $5) NoInfo (srcspan $1 $>) }-     | Exp2 '..' Exp2 '..<' Exp2 { Range $1 (Just $3) (UpToExclusive $5) NoInfo (srcspan $1 $>) }-     | Exp2 '..' Exp2 '..>' Exp2 { Range $1 (Just $3) (DownToExclusive $5) NoInfo (srcspan $1 $>) }+     | Exp2 '...' Exp2           { Range $1 Nothing (ToInclusive $3) (NoInfo,NoInfo) (srcspan $1 $>) }+     | Exp2 '..<' Exp2           { Range $1 Nothing (UpToExclusive $3) (NoInfo,NoInfo) (srcspan $1 $>) }+     | Exp2 '..>' Exp2           { Range $1 Nothing (DownToExclusive $3) (NoInfo,NoInfo) (srcspan $1 $>) }+     | Exp2 '..' Exp2 '...' Exp2 { Range $1 (Just $3) (ToInclusive $5) (NoInfo,NoInfo) (srcspan $1 $>) }+     | Exp2 '..' Exp2 '..<' Exp2 { Range $1 (Just $3) (UpToExclusive $5) (NoInfo,NoInfo) (srcspan $1 $>) }+     | Exp2 '..' Exp2 '..>' Exp2 { Range $1 (Just $3) (DownToExclusive $5) (NoInfo,NoInfo) (srcspan $1 $>) }      | Exp2 '..' Atom            {% twoDotsRange $2 }      | Atom '..' Exp2            {% twoDotsRange $2 }      | '-' Exp2@@ -604,7 +605,7 @@ Apply_ :: { UncheckedExp }        : ApplyList { case $1 of                        ((Constr n [] _ loc1):_) -> Constr n (tail $1) NoInfo (srcspan loc1 (last $1))-                       _                -> foldl1 (\f x -> Apply f x NoInfo NoInfo (srcspan f x)) $1 }+                       _                -> foldl1 (\f x -> Apply f x NoInfo (NoInfo, NoInfo) (srcspan f x)) $1 }  ApplyList :: { [UncheckedExp] }           : ApplyList Atom %prec juxtprec@@ -625,9 +626,9 @@                         StringLit (encode s) loc }      | '(' Exp ')' FieldAccesses        { foldl (\x (y, _) -> Project y x NoInfo (srclocOf x))-               (Parens $2 (srcspan $1 $3))+               (Parens $2 (srcspan $1 ($3:map snd $>)))                $4 }-     | '(' Exp ')[' DimIndices ']'    { Index (Parens $2 $1) $4 NoInfo (srcspan $1 $>) }+     | '(' Exp ')[' DimIndices ']'    { Index (Parens $2 $1) $4 (NoInfo, NoInfo) (srcspan $1 $>) }      | '(' Exp ',' Exps1 ')'          { TupLit ($2 : fst $4 : snd $4) (srcspan $1 $>) }      | '('      ')'                   { TupLit [] (srcspan $1 $>) }      | '[' Exps1 ']'                  { ArrayLit (fst $2:snd $2) NoInfo (srcspan $1 $>) }@@ -636,7 +637,7 @@      | QualVarSlice FieldAccesses        { let ((v, vloc),slice,loc) = $1          in foldl (\x (y, _) -> Project y x NoInfo (srcspan x (srclocOf x)))-                  (Index (Var v NoInfo vloc) slice NoInfo (srcspan vloc loc))+                  (Index (Var v NoInfo vloc) slice (NoInfo, NoInfo) (srcspan vloc loc))                   $2 }      | QualName        { Var (fst $1) NoInfo (snd $1) }@@ -651,12 +652,12 @@      | '(' '-' ')'         { OpSection (qualName (nameFromString "-")) NoInfo (srcspan $1 $>) }      | '(' Exp2 '-' ')'-        { OpSectionLeft (qualName (nameFromString "-"))-           NoInfo $2 (NoInfo, NoInfo) NoInfo (srcspan $1 $>) }+       { OpSectionLeft (qualName (nameFromString "-"))+         NoInfo $2 (NoInfo, NoInfo) (NoInfo, NoInfo) (srcspan $1 $>) }      | '(' BinOp Exp2 ')'        { OpSectionRight (fst $2) NoInfo $3 (NoInfo, NoInfo) NoInfo (srcspan $1 $>) }      | '(' Exp2 BinOp ')'-       { OpSectionLeft (fst $3) NoInfo $2 (NoInfo, NoInfo) NoInfo (srcspan $1 $>) }+       { OpSectionLeft (fst $3) NoInfo $2 (NoInfo, NoInfo) (NoInfo, NoInfo) (srcspan $1 $>) }      | '(' BinOp ')'        { OpSection (fst $2) NoInfo (srcspan $1 $>) } @@ -717,11 +718,12 @@  LetExp :: { UncheckedExp }      : let Pattern '=' Exp LetBody-                      { LetPat $2 $4 $5 NoInfo (srcspan $1 $>) }+                      { LetPat $2 $4 $5 (NoInfo, NoInfo) (srcspan $1 $>) }       | let id TypeParams FunParams1 maybeAscription(TypeExpDecl) '=' Exp LetBody        { let L _ (ID name) = $2-         in LetFun name ($3, fst $4 : snd $4, (fmap declaredType $5), NoInfo, $7) $8 (srcspan $1 $>) }+         in LetFun name ($3, fst $4 : snd $4, (fmap declaredType $5), NoInfo, $7)+            $8 NoInfo (srcspan $1 $>) }       | let VarSlice '=' Exp LetBody                       { let ((v,_),slice,loc) = $2; ident = Ident v NoInfo loc@@ -733,7 +735,7 @@  MatchExp :: { UncheckedExp }           : match Exp Cases  { let loc = srcspan $1 (NE.toList $>)-                               in Match $2 $> NoInfo loc  }+                               in Match $2 $> (NoInfo, NoInfo) loc  }  Cases :: { NE.NonEmpty (CaseBase NoInfo Name) }        : Case  %prec caseprec { $1 NE.:| [] }@@ -1071,7 +1073,7 @@ binOpName (L loc (SYMBOL _ qs op)) = (QualName qs op, loc)  binOp x (L loc (SYMBOL _ qs op)) y =-  BinOp (QualName qs op, loc) NoInfo (x, NoInfo) (y, NoInfo) NoInfo $+  BinOp (QualName qs op, loc) NoInfo (x, NoInfo) (y, NoInfo) NoInfo NoInfo $   srcspan x y  getTokens :: ParserMonad ([L Token], Pos)
+ src/Language/Futhark/Prelude.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+-- | The Futhark Prelude Library embedded embedded as strings read+-- during compilation of the Futhark compiler.  The advantage is that+-- the prelude can be accessed without reading it from disk, thus+-- saving users from include path headaches.+module Language.Futhark.Prelude (prelude) where++import Data.FileEmbed+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified System.FilePath.Posix as Posix++import Futhark.Util (toPOSIX)++-- | Prelude embedded as 'T.Text' values, one for every file.+prelude :: [(Posix.FilePath, T.Text)]+prelude = map fixup prelude_bs+  where prelude_bs = $(embedDir "prelude")+        fixup (path, s) = ("/prelude" Posix.</> toPOSIX path, T.decodeUtf8 s)
src/Language/Futhark/Pretty.hs view
@@ -118,6 +118,9 @@ instance Pretty (ShapeDecl Int32) where   ppr (ShapeDecl ds) = mconcat (map (brackets . ppr) ds) +instance Pretty (ShapeDecl Bool) where+  ppr (ShapeDecl ds) = mconcat (map (brackets . ppr) ds)+ instance Pretty (ShapeDecl dim) => Pretty (ScalarTypeBase dim as) where   ppr = pprPrec 0   pprPrec _ (Prim et) = ppr et@@ -126,15 +129,16 @@     ppr u <> ppr (qualNameFromTypeName et) <+> spread (map (pprPrec 3) targs)   pprPrec _ (Record fs)     | Just ts <- areTupleFields fs =-        parens $ commasep $ map ppr ts+        oneLine (parens $ commasep $ map ppr ts)+        <|> parens (align $ mconcat $ punctuate (text "," <> line) $ map ppr ts)     | otherwise =         oneLine (braces $ commasep fs')-        <|> braces (mconcat $ punctuate (text "," <> line) fs')+        <|> braces (align $ mconcat $ punctuate (text "," <> line) fs')     where ppField (name, t) = text (nameToString name) <> colon <+> align (ppr t)           fs' = map ppField $ M.toList fs   pprPrec p (Arrow _ (Named v) t1 t2) =     parensIf (p > 1) $-    parens (pprName v <> colon <+> ppr t1) <+/> text "->" <+> pprPrec 1 t2+    parens (pprName v <> colon <+> align (ppr t1)) <+/> text "->" <+> pprPrec 1 t2   pprPrec p (Arrow _ Unnamed t1 t2) =     parensIf (p > 1) $ pprPrec 2 t1 <+/> text "->" <+> pprPrec 1 t2   pprPrec p (Sum cs) =@@ -146,7 +150,7 @@  instance Pretty (ShapeDecl dim) => Pretty (TypeBase dim as) where   ppr = pprPrec 0-  pprPrec _ (Array _ u at shape) = ppr u <> ppr shape <> pprPrec 1 at+  pprPrec _ (Array _ u at shape) = ppr u <> ppr shape <> align (pprPrec 1 at)   pprPrec p (Scalar t) = pprPrec p t  instance Pretty (ShapeDecl dim) => Pretty (TypeArg dim) where@@ -208,11 +212,18 @@  instance (Eq vn, IsName vn, Annot f) => Pretty (ExpBase f vn) where   ppr = pprPrec (-1)-  pprPrec _ (Var name _ _) = ppr name+  pprPrec _ (Var name t _) = ppr name <> inst+    where inst = case unAnnot t of+                   Just t'+                     | isEnvVarSet "FUTHARK_COMPILER_DEBUGGING" False ->+                         text "@" <> parens (align $ ppr t')+                   _ -> mempty   pprPrec _ (Parens e _) = align $ parens $ ppr e   pprPrec _ (QualParens (v, _) e _) = ppr v <> text "." <> align (parens $ ppr e)-  pprPrec p (Ascript e t _ _) =-    parensIf (p /= -1) $ pprPrec 0 e <+> colon <+> pprPrec 0 t+  pprPrec p (Ascript e t _) =+    parensIf (p /= -1) $ pprPrec 0 e <+> text ":" <+> pprPrec 0 t+  pprPrec p (Coerce e t _ _) =+    parensIf (p /= -1) $ pprPrec 0 e <+> text ":>" <+> pprPrec 0 t   pprPrec _ (Literal v _) = ppr v   pprPrec _ (IntLit v _ _) = ppr v   pprPrec _ (FloatLit v _ _) = ppr v@@ -224,8 +235,13 @@     | otherwise                     = braces $ commasep $ map ppr fs     where fieldArray (RecordFieldExplicit _ e _) = hasArrayLit e           fieldArray RecordFieldImplicit{} = False-  pprPrec _ (ArrayLit es _ _) =-    brackets $ commasep $ map ppr es+  pprPrec _ (ArrayLit es info _) =+    brackets (commasep $ map ppr es) <> info'+    where info' = case unAnnot info of+                    Just t+                      | isEnvVarSet "FUTHARK_COMPILER_DEBUGGING" False ->+                          text "@" <> parens (align $ ppr t)+                    _ -> mempty   pprPrec _ (StringLit s _) =     text $ show $ decode s   pprPrec p (Range start maybe_step end _ _) =@@ -235,13 +251,13 @@       DownToExclusive end' -> text "..>" <> ppr end'       ToInclusive     end' -> text "..." <> ppr end'       UpToExclusive   end' -> text "..<" <> ppr end'-  pprPrec p (BinOp (bop, _) _ (x,_) (y,_) _ _) = prettyBinOp p bop x y+  pprPrec p (BinOp (bop,_) _ (x,_) (y,_) _ _ _) = prettyBinOp p bop x y   pprPrec _ (Project k e _ _) = ppr e <> text "." <> ppr k   pprPrec _ (If c t f _ _) = text "if" <+> ppr c </>                              text "then" <+> align (ppr t) </>                              text "else" <+> align (ppr f)   pprPrec p (Apply f arg _ _ _) =-    parensIf (p >= 10) $ ppr f <+> pprPrec 10 arg+    parensIf (p >= 10) $ pprPrec 0 f <+/> pprPrec 10 arg   pprPrec _ (Negate e _) = text "-" <> ppr e   pprPrec p (LetPat pat e body _ _) =     parensIf (p /= -1) $ align $@@ -258,7 +274,7 @@                         Match{}     -> True                         ArrayLit{}  -> False                         _           -> hasArrayLit e-  pprPrec _ (LetFun fname (tparams, params, retdecl, rettype, e) body _) =+  pprPrec _ (LetFun fname (tparams, params, retdecl, rettype, e) body _ _) =     text "let" <+> pprName fname <+> spread (map ppr tparams ++ map ppr params) <>     retdecl' <+> equals </> indent 2 (ppr e) </>     letBody body@@ -302,9 +318,10 @@     where p name = text "." <> ppr name   pprPrec _ (IndexSection idxs _ _) =     parens $ text "." <> brackets (commasep (map ppr idxs))-  pprPrec _ (DoLoop pat initexp form loopbody _) =-    text "loop" <+> ppr pat <+>-    equals <+> ppr initexp <+> ppr form <+> text "do" </>+  pprPrec _ (DoLoop sizeparams pat initexp form loopbody _ _) =+    text "loop" <+>+    align (spread (map (brackets . pprName) sizeparams) <+/>+           ppr pat <+> equals <+/> ppr initexp <+/> ppr form <+> text "do") </>     indent 2 (ppr loopbody)   pprPrec _ (Constr n cs _ _) = text "#" <> ppr n <+> sep (map ppr cs)   pprPrec _ (Match e cs _ _) = text "match" <+> ppr e </> (stack . map ppr) (NE.toList cs)@@ -385,11 +402,11 @@ instance (Eq vn, IsName vn, Annot f) => Pretty (ValBindBase f vn) where   ppr (ValBind entry name retdecl rettype tparams args body _ _) =     text fun <+> pprName name <+>-    spread (map ppr tparams ++ map ppr args) <> retdecl' <> text " =" </>+    align (sep (map ppr tparams ++ map ppr args)) <> retdecl' <> text " =" </>     indent 2 (ppr body)     where fun | isJust entry = "entry"               | otherwise    = "let"-          retdecl' = case (ppr <$> unAnnot rettype) `mplus` (ppr <$> retdecl) of+          retdecl' = case (ppr . fst <$> unAnnot rettype) `mplus` (ppr <$> retdecl) of                        Just rettype' -> text ":" <+> rettype'                        Nothing       -> mempty 
src/Language/Futhark/Query.hs view
@@ -88,14 +88,14 @@               patternDefs pat             Lambda params _ _ _ _ ->               mconcat (map patternDefs params)-            LetFun name (tparams, params, _, Info ret, _) _ loc ->+            LetFun name (tparams, params, _, Info ret, _) _ _ loc ->               let name_t = foldFunType (map patternStructType params) ret               in M.singleton name (DefBound $ BoundTerm name_t (locOf loc)) <>                  mconcat (map typeParamDefs tparams) <>                  mconcat (map patternDefs params)             LetWith v _ _ _ _ _ _ ->               identDefs v-            DoLoop merge _ form _ _ ->+            DoLoop _ merge _ form _ _ _ ->               patternDefs merge <>               case form of                 For i _ -> identDefs i@@ -112,7 +112,7 @@   expDefs (valBindBody vbind)   where vbind_t =           foldFunType (map patternStructType (valBindParams vbind)) $-          unInfo $ valBindRetType vbind+          fst $ unInfo $ valBindRetType vbind  typeBindDefs :: TypeBind -> Defs typeBindDefs tbind =@@ -267,10 +267,13 @@   | a `contains` pos = Just $ RawAtName (qualName $ identName a) (locOf a)   | b `contains` pos = Just $ RawAtName (qualName $ identName b) (locOf b) -atPosInExp (DoLoop merge _ _ _ _) pos+atPosInExp (DoLoop _ merge _ _ _ _ _) pos   | merge `contains` pos = atPosInPattern merge pos -atPosInExp (Ascript _ tdecl _ _) pos+atPosInExp (Ascript _ tdecl _) pos+  | tdecl `contains` pos = atPosInTypeExp (declaredType tdecl) pos++atPosInExp (Coerce _ tdecl _ _) pos   | tdecl `contains` pos = atPosInTypeExp (declaredType tdecl) pos  atPosInExp e pos = do
src/Language/Futhark/Semantic.hs view
@@ -136,6 +136,11 @@   Env vt1 tt1 st1 mt1 nt1 <> Env vt2 tt2 st2 mt2 nt2 =     Env (vt1<>vt2) (tt1<>tt2) (st1<>st2) (mt1<>mt2) (nt1<>nt2) +instance Pretty Namespace where+  ppr Term = text "name"+  ppr Type = text "type"+  ppr Signature = text "module type"+ instance Monoid Env where   mempty = Env mempty mempty mempty mempty mempty 
src/Language/Futhark/Syntax.hs view
@@ -111,12 +111,16 @@ -- | Convenience class for deriving 'Show' instances for the AST. class (Show vn,        Show (f VName),-       Show (f Diet),+       Show (f (Diet, Maybe VName)),        Show (f String),        Show (f [VName]),+       Show (f ([VName], [VName])),        Show (f PatternType),+       Show (f (PatternType, [VName])),+       Show (f (StructType, [VName])),        Show (f Int),        Show (f StructType),+       Show (f (StructType, Maybe VName)),        Show (f (Aliasing, StructType)),        Show (f (M.Map VName VName)),        Show (f Uniqueness)) => Showable f vn where@@ -227,6 +231,8 @@   traverse _ (ConstDim x) = pure $ ConstDim x   traverse _ AnyDim = pure AnyDim +-- Note that the notion of unifyDims here is intentionally not what we+-- use when we do real type unification in the type checker. instance ArrayDim (DimDecl VName) where   unifyDims AnyDim y = Just y   unifyDims x AnyDim = Just x@@ -621,22 +627,37 @@             -- ^ Array literals, e.g., @[ [1+x, 3], [2, 1+4] ]@.             -- Second arg is the row type of the rows of the array. -            | Range (ExpBase f vn) (Maybe (ExpBase f vn)) (Inclusiveness (ExpBase f vn)) (f PatternType) SrcLoc+            | Range (ExpBase f vn) (Maybe (ExpBase f vn)) (Inclusiveness (ExpBase f vn))+              (f PatternType, f [VName]) SrcLoc              | Var (QualName vn) (f PatternType) SrcLoc -            | Ascript (ExpBase f vn) (TypeDeclBase f vn) (f PatternType) SrcLoc+            | Ascript (ExpBase f vn) (TypeDeclBase f vn) SrcLoc             -- ^ Type ascription: @e : t@. -            | LetPat (PatternBase f vn) (ExpBase f vn) (ExpBase f vn) (f PatternType) SrcLoc+            | Coerce (ExpBase f vn) (TypeDeclBase f vn) (f PatternType, f [VName]) SrcLoc+            -- ^ Size coercion: @e :> t@. -            | LetFun vn ([TypeParamBase vn], [PatternBase f vn], Maybe (TypeExp vn), f StructType, ExpBase f vn)-              (ExpBase f vn) SrcLoc+            | LetPat (PatternBase f vn) (ExpBase f vn) (ExpBase f vn)+              (f PatternType, f [VName]) SrcLoc -            | If     (ExpBase f vn) (ExpBase f vn) (ExpBase f vn) (f PatternType) SrcLoc+            | LetFun vn ([TypeParamBase vn],+                         [PatternBase f vn],+                         Maybe (TypeExp vn),+                         f StructType,+                         ExpBase f vn)+              (ExpBase f vn) (f PatternType) SrcLoc -            | Apply (ExpBase f vn) (ExpBase f vn) (f Diet) (f PatternType) SrcLoc+            | If (ExpBase f vn) (ExpBase f vn) (ExpBase f vn) (f PatternType, f [VName]) SrcLoc +            | Apply (ExpBase f vn) (ExpBase f vn)+              (f (Diet, Maybe VName)) (f PatternType, f [VName]) SrcLoc+              -- ^ The @Maybe VName@ is a possible existential size+              -- that is instantiated by this argument..+              --+              -- The @[VName]@ are the existential sizes that come+              -- into being at this call site.+             | Negate (ExpBase f vn) SrcLoc               -- ^ Numeric negation (ugly special case; Haskell did it first). @@ -645,11 +666,11 @@              | OpSection (QualName vn) (f PatternType) SrcLoc               -- ^ @+@; first two types are operands, third is result.-            | OpSectionLeft (QualName vn) (f PatternType)-              (ExpBase f vn) (f StructType, f StructType) (f PatternType) SrcLoc+            | OpSectionLeft (QualName vn) (f PatternType) (ExpBase f vn)+              (f (StructType, Maybe VName), f StructType) (f PatternType, f [VName]) SrcLoc               -- ^ @2+@; first type is operand, second is result.-            | OpSectionRight (QualName vn) (f PatternType)-              (ExpBase f vn) (f StructType, f StructType) (f PatternType) SrcLoc+            | OpSectionRight (QualName vn) (f PatternType) (ExpBase f vn)+              (f StructType, f (StructType, Maybe VName)) (f PatternType) SrcLoc               -- ^ @+2@; first type is operand, second is result.             | ProjectSection [Name] (f PatternType) SrcLoc               -- ^ Field projection as a section: @(.x.y.z)@.@@ -657,15 +678,18 @@               -- ^ Array indexing as a section: @(.[i,j])@.              | DoLoop-              (PatternBase f vn) -- Merge variable pattern+              [VName] -- Size parameters.+              (PatternBase f vn) -- Merge variable pattern.               (ExpBase f vn) -- Initial values of merge variables.               (LoopFormBase f vn) -- Do or while loop.               (ExpBase f vn) -- Loop body.+              (f (PatternType, [VName])) -- Return type.               SrcLoc              | BinOp (QualName vn, SrcLoc) (f PatternType)-              (ExpBase f vn, f StructType) (ExpBase f vn, f StructType)-              (f PatternType) SrcLoc+              (ExpBase f vn, f (StructType, Maybe VName))+              (ExpBase f vn, f (StructType, Maybe VName))+              (f PatternType) (f [VName]) SrcLoc              | Project Name (ExpBase f vn) (f PatternType) SrcLoc @@ -674,7 +698,7 @@                       [DimIndexBase f vn] (ExpBase f vn)                       (ExpBase f vn) (f PatternType) SrcLoc -            | Index (ExpBase f vn) [DimIndexBase f vn] (f PatternType) SrcLoc+            | Index (ExpBase f vn) [DimIndexBase f vn] (f PatternType, f [VName]) SrcLoc              | Update (ExpBase f vn) [DimIndexBase f vn] (ExpBase f vn) SrcLoc @@ -694,7 +718,8 @@             | Constr Name [ExpBase f vn] (f PatternType) SrcLoc             -- ^ An n-ary value constructor. -            | Match (ExpBase f vn) (NE.NonEmpty (CaseBase f vn)) (f PatternType) SrcLoc+            | Match (ExpBase f vn) (NE.NonEmpty (CaseBase f vn))+              (f PatternType, f [VName]) SrcLoc             -- ^ A match expression. deriving instance Showable f vn => Show (ExpBase f vn) deriving instance Eq (ExpBase NoInfo VName)@@ -712,14 +737,15 @@   locOf (ArrayLit _ _ pos)             = locOf pos   locOf (StringLit _ loc)              = locOf loc   locOf (Range _ _ _ _ pos)            = locOf pos-  locOf (BinOp _ _ _ _ _ pos)          = locOf pos+  locOf (BinOp _ _ _ _ _ _ loc)        = locOf loc   locOf (If _ _ _ _ pos)               = locOf pos   locOf (Var _ _ loc)                  = locOf loc-  locOf (Ascript _ _ _ loc)            = locOf loc+  locOf (Ascript _ _ loc)              = locOf loc+  locOf (Coerce _ _ _ loc)             = locOf loc   locOf (Negate _ pos)                 = locOf pos-  locOf (Apply _ _ _ _ pos)            = locOf pos+  locOf (Apply _ _ _ _ loc)            = locOf loc   locOf (LetPat _ _ _ _ loc)           = locOf loc-  locOf (LetFun _ _ _ loc)             = locOf loc+  locOf (LetFun _ _ _ _ loc)           = locOf loc   locOf (LetWith _ _ _ _ _ _ loc)      = locOf loc   locOf (Index _ _ _ loc)              = locOf loc   locOf (Update _ _ _ pos)             = locOf pos@@ -730,7 +756,7 @@   locOf (OpSectionRight _ _ _ _ _ loc) = locOf loc   locOf (ProjectSection _ _ loc)       = locOf loc   locOf (IndexSection _ _ loc)         = locOf loc-  locOf (DoLoop _ _ _ _ pos)           = locOf pos+  locOf (DoLoop _ _ _ _ _ _ loc)       = locOf loc   locOf (Unsafe _ loc)                 = locOf loc   locOf (Assert _ _ _ loc)             = locOf loc   locOf (Constr _ _ _ loc)             = locOf loc@@ -806,7 +832,7 @@                                 -- that are no longer in scope.                                 , valBindName       :: vn                                 , valBindRetDecl    :: Maybe (TypeExp vn)-                                , valBindRetType    :: f StructType+                                , valBindRetType    :: f (StructType, [VName])                                 , valBindTypeParams :: [TypeParamBase vn]                                 , valBindParams     :: [PatternBase f vn]                                 , valBindBody       :: ExpBase f vn
src/Language/Futhark/Traversals.hs view
@@ -22,6 +22,8 @@ module Language.Futhark.Traversals   ( ASTMapper(..)   , ASTMappable(..)+  , identityMapper+  , bareExp   ) where  import qualified Data.Set                as S@@ -40,6 +42,15 @@   , mapOnPatternType :: PatternType -> m PatternType   } +-- | An 'ASTMapper' that just leaves its input unchanged.+identityMapper :: Monad m => ASTMapper m+identityMapper = ASTMapper { mapOnExp = return+                           , mapOnName = return+                           , mapOnQualName = return+                           , mapOnStructType = return+                           , mapOnPatternType = return+                           }+ class ASTMappable x where   -- | Map a monadic action across the immediate children of an   -- object.  Importantly, the 'astMap' action is not invoked for@@ -70,36 +81,40 @@     RecordLit <$> astMap tv fields <*> pure loc   astMap tv (ArrayLit els t loc) =     ArrayLit <$> mapM (mapOnExp tv) els <*> traverse (mapOnPatternType tv) t <*> pure loc-  astMap tv (Range start next end t loc) =+  astMap tv (Range start next end (t, ext) loc) =     Range <$> mapOnExp tv start <*> traverse (mapOnExp tv) next <*>-    traverse (mapOnExp tv) end <*> traverse (mapOnPatternType tv) t <*> pure loc-  astMap tv (Ascript e tdecl t loc) =-    Ascript <$> mapOnExp tv e <*> astMap tv tdecl <*>-    traverse (mapOnPatternType tv) t <*> pure loc-  astMap tv (BinOp (fname, fname_loc) t (x,xt) (y,yt) (Info rt) loc) =+    traverse (mapOnExp tv) end <*>+    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+  astMap tv (Ascript e tdecl loc) =+    Ascript <$> mapOnExp tv e <*> astMap tv tdecl <*> pure loc+  astMap tv (Coerce e tdecl (t, ext) loc) =+    Coerce <$> mapOnExp tv e <*> astMap tv tdecl <*>+    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+  astMap tv (BinOp (fname, fname_loc) t (x,Info(xt,xext)) (y,Info(yt,yext)) (Info rt) ext loc) =     BinOp <$> ((,) <$> mapOnQualName tv fname <*> pure fname_loc) <*>     traverse (mapOnPatternType tv) t <*>-    ((,) <$> mapOnExp tv x <*> traverse (mapOnStructType tv) xt) <*>-    ((,) <$> mapOnExp tv y <*> traverse (mapOnStructType tv) yt) <*>-    (Info <$> mapOnPatternType tv rt) <*> pure loc+    ((,) <$> mapOnExp tv x <*>+     (Info <$> ((,) <$> mapOnStructType tv xt <*> pure xext))) <*>+    ((,) <$> mapOnExp tv y <*>+     (Info <$> ((,) <$> mapOnStructType tv yt <*> pure yext))) <*>+    (Info <$> mapOnPatternType tv rt) <*> pure ext <*> pure loc   astMap tv (Negate x loc) =     Negate <$> mapOnExp tv x <*> pure loc-  astMap tv (If c texp fexp t loc) =+  astMap tv (If c texp fexp (t, ext) loc) =     If <$> mapOnExp tv c <*> mapOnExp tv texp <*> mapOnExp tv fexp <*>-    traverse (mapOnPatternType tv) t <*> pure loc-  astMap tv (Apply f arg d (Info t) loc) =-    Apply <$> mapOnExp tv f <*> mapOnExp tv arg <*>-    pure d <*> (Info <$> mapOnPatternType tv t) <*>-    pure loc-  astMap tv (LetPat pat e body t loc) =-    LetPat <$> astMap tv pat <*> mapOnExp tv e <*>-    mapOnExp tv body <*> traverse (mapOnPatternType tv) t <*> pure loc-  astMap tv (LetFun name (fparams, params, ret, t, e) body loc) =+    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+  astMap tv (Apply f arg d (Info t, ext) loc) =+    Apply <$> mapOnExp tv f <*> mapOnExp tv arg <*> pure d <*>+    ((,) <$> (Info <$> mapOnPatternType tv t) <*> pure ext) <*> pure loc+  astMap tv (LetPat pat e body (t, ext) loc) =+    LetPat <$> astMap tv pat <*> mapOnExp tv e <*> mapOnExp tv body <*>+    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+  astMap tv (LetFun name (fparams, params, ret, t, e) body body_t loc) =     LetFun <$> mapOnName tv name <*>     ((,,,,) <$> mapM (astMap tv) fparams <*> mapM (astMap tv) params <*>      traverse (astMap tv) ret <*> traverse (mapOnStructType tv) t <*>      mapOnExp tv e) <*>-    mapOnExp tv body <*> pure loc+    mapOnExp tv body <*> traverse (mapOnPatternType tv) body_t <*> pure loc   astMap tv (LetWith dest src idxexps vexp body t loc) =     LetWith <$>     astMap tv dest <*> astMap tv src <*>@@ -113,49 +128,48 @@     mapOnExp tv v <*> (Info <$> mapOnPatternType tv t) <*> pure loc   astMap tv (Project field e t loc) =     Project field <$> mapOnExp tv e <*> traverse (mapOnPatternType tv) t <*> pure loc-  astMap tv (Index arr idxexps t loc) =-    Index <$>-    mapOnExp tv arr <*>-    mapM (astMap tv) idxexps <*>-    traverse (mapOnPatternType tv) t <*>-    pure loc+  astMap tv (Index arr idxexps (t, ext) loc) =+    Index <$> mapOnExp tv arr <*> mapM (astMap tv) idxexps <*>+    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc   astMap tv (Unsafe e loc) =     Unsafe <$> mapOnExp tv e <*> pure loc   astMap tv (Assert e1 e2 desc loc) =     Assert <$> mapOnExp tv e1 <*> mapOnExp tv e2 <*> pure desc <*> pure loc   astMap tv (Lambda params body ret t loc) =     Lambda <$> mapM (astMap tv) params <*>-    astMap tv body <*> traverse (astMap tv) ret <*>+    mapOnExp tv body <*> traverse (astMap tv) ret <*>     traverse (traverse $ mapOnStructType tv) t <*> pure loc   astMap tv (OpSection name t loc) =     OpSection <$> mapOnQualName tv name <*>     traverse (mapOnPatternType tv) t <*> pure loc-  astMap tv (OpSectionLeft name t arg (t1a, t1b) t2 loc) =+  astMap tv (OpSectionLeft name t arg (Info (t1a, argext), t1b) (t2, retext) loc) =     OpSectionLeft <$> mapOnQualName tv name <*>     traverse (mapOnPatternType tv) t <*> mapOnExp tv arg <*>-    ((,) <$> traverse (mapOnStructType tv) t1a <*>-      traverse (mapOnStructType tv) t1b) <*>-    traverse (mapOnPatternType tv) t2 <*> pure loc-  astMap tv (OpSectionRight name t arg (t1a, t1b) t2 loc) =+    ((,) <$>+     (Info <$> ((,) <$> mapOnStructType tv t1a <*> pure argext)) <*>+     traverse (mapOnStructType tv) t1b) <*>+    ((,) <$> traverse (mapOnPatternType tv) t2 <*> pure retext) <*> pure loc+  astMap tv (OpSectionRight name t arg (t1a, Info (t1b,argext)) t2 loc) =     OpSectionRight <$> mapOnQualName tv name <*>     traverse (mapOnPatternType tv) t <*> mapOnExp tv arg <*>-    ((,) <$> traverse (mapOnStructType tv) t1a <*>-     traverse (mapOnStructType tv) t1b) <*>+    ((,) <$>+     traverse (mapOnStructType tv) t1a <*>+     (Info <$> ((,) <$> mapOnStructType tv t1b <*> pure argext))) <*>     traverse (mapOnPatternType tv) t2 <*> pure loc   astMap tv (ProjectSection fields t loc) =     ProjectSection fields <$> traverse (mapOnPatternType tv) t <*> pure loc   astMap tv (IndexSection idxs t loc) =     IndexSection <$> mapM (astMap tv) idxs <*>     traverse (mapOnPatternType tv) t <*> pure loc-  astMap tv (DoLoop mergepat mergeexp form loopbody loc) =-    DoLoop <$> astMap tv mergepat <*>-    mapOnExp tv mergeexp <*> astMap tv form <*>-    mapOnExp tv loopbody <*> pure loc+  astMap tv (DoLoop sparams mergepat mergeexp form loopbody (Info (ret, ext)) loc) =+    DoLoop <$> mapM (mapOnName tv) sparams <*> astMap tv mergepat <*>+    mapOnExp tv mergeexp <*> astMap tv form <*> mapOnExp tv loopbody <*>+    (Info <$> ((,) <$> mapOnPatternType tv ret <*> pure ext)) <*> pure loc   astMap tv (Constr name es ts loc) =     Constr name <$> traverse (mapOnExp tv) es <*> traverse (mapOnPatternType tv) ts <*> pure loc-  astMap tv (Match e cases t loc) =+  astMap tv (Match e cases (t, ext) loc) =     Match <$> mapOnExp tv e <*> astMap tv cases-          <*> traverse (mapOnPatternType tv) t <*> pure loc+          <*> ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc  instance ASTMappable (LoopFormBase Info VName) where   astMap tv (For i bound) = For <$> astMap tv i <*> astMap tv bound@@ -298,3 +312,106 @@  instance (ASTMappable a, ASTMappable b, ASTMappable c) => ASTMappable (a,b,c) where   astMap tv (x,y,z) = (,,) <$> astMap tv x <*> astMap tv y <*> astMap tv z++-- It would be lovely if the following code would be written in terms+-- of ASTMappable, but unfortunately it involves changing the Info+-- functor.  For simplicity, the general traversals do not support+-- that.  Sometimes a little duplication is better than an overly+-- complex abstraction.  The types ensure that this will be correct+-- anyway, so it's just tedious, and not actually fragile.++bareTypeDecl :: TypeDeclBase Info VName -> TypeDeclBase NoInfo VName+bareTypeDecl (TypeDecl te _) = TypeDecl te NoInfo++bareField :: FieldBase Info VName -> FieldBase NoInfo VName+bareField (RecordFieldExplicit name e loc) =+  RecordFieldExplicit name (bareExp e) loc+bareField (RecordFieldImplicit name _ loc) =+  RecordFieldImplicit name NoInfo loc++barePat :: PatternBase Info VName -> PatternBase NoInfo VName+barePat (TuplePattern ps loc) = TuplePattern (map barePat ps) loc+barePat (RecordPattern fs loc) = RecordPattern (map (fmap barePat) fs) loc+barePat (PatternParens p loc) = PatternParens (barePat p) loc+barePat (Id v _ loc) = Id v NoInfo loc+barePat (Wildcard _ loc) = Wildcard NoInfo loc+barePat (PatternAscription pat (TypeDecl t _) loc) =+  PatternAscription (barePat pat) (TypeDecl t NoInfo) loc+barePat (PatternLit e _ loc) = PatternLit (bareExp e) NoInfo loc+barePat (PatternConstr c _ ps loc) = PatternConstr c NoInfo (map barePat ps) loc++bareDimIndex :: DimIndexBase Info VName -> DimIndexBase NoInfo VName+bareDimIndex (DimFix e) =+  DimFix $ bareExp e+bareDimIndex (DimSlice x y z) =+  DimSlice (bareExp <$> x) (bareExp <$> y) (bareExp <$> z)++bareLoopForm :: LoopFormBase Info VName -> LoopFormBase NoInfo VName+bareLoopForm (For (Ident i _ loc) e) = For (Ident i NoInfo loc) (bareExp e)+bareLoopForm (ForIn pat e) = ForIn (barePat pat) (bareExp e)+bareLoopForm (While e) = While (bareExp e)++bareCase :: CaseBase Info VName -> CaseBase NoInfo VName+bareCase (CasePat pat e loc) = CasePat (barePat pat) (bareExp e) loc++-- | Remove all annotations from an expression, but retain the+-- name/scope information.+bareExp :: ExpBase Info VName -> ExpBase NoInfo VName+bareExp (Var name _ loc) = Var name NoInfo loc+bareExp (Literal v loc) = Literal v loc+bareExp (IntLit val _ loc) = IntLit val NoInfo loc+bareExp (FloatLit val _ loc) = FloatLit val NoInfo loc+bareExp (Parens e loc) = Parens (bareExp e) loc+bareExp (QualParens name e loc) = QualParens name (bareExp e) loc+bareExp (TupLit els loc) = TupLit (map bareExp els) loc+bareExp (StringLit vs loc) = StringLit vs loc+bareExp (RecordLit fields loc) = RecordLit (map bareField fields) loc+bareExp (ArrayLit els _ loc) = ArrayLit (map bareExp els) NoInfo loc+bareExp (Range start next end _ loc) =+  Range (bareExp start) (fmap bareExp next)+  (fmap bareExp end) (NoInfo, NoInfo) loc+bareExp (Ascript e tdecl loc) =+  Ascript (bareExp e) (bareTypeDecl tdecl) loc+bareExp (Coerce e tdecl _ loc) =+  Coerce (bareExp e) (bareTypeDecl tdecl) (NoInfo, NoInfo) loc+bareExp (BinOp fname _ (x,_) (y,_) _ _ loc) =+  BinOp fname NoInfo (bareExp x, NoInfo) (bareExp y, NoInfo) NoInfo NoInfo loc+bareExp (Negate x loc) = Negate (bareExp x) loc+bareExp (If c texp fexp _ loc) =+  If (bareExp c) (bareExp texp) (bareExp fexp) (NoInfo, NoInfo) loc+bareExp (Apply f arg _ _ loc) =+  Apply (bareExp f) (bareExp arg) NoInfo (NoInfo, NoInfo) loc+bareExp (LetPat pat e body _ loc) =+  LetPat (barePat pat) (bareExp e) (bareExp body) (NoInfo, NoInfo) loc+bareExp (LetFun name (fparams, params, ret, _, e) body _ loc) =+  LetFun name (fparams, map barePat params, ret, NoInfo, bareExp e) (bareExp body) NoInfo loc+bareExp (LetWith (Ident dest _ destloc) (Ident src _ srcloc) idxexps vexp body _ loc) =+  LetWith (Ident dest NoInfo destloc) (Ident src NoInfo srcloc)+  (map bareDimIndex idxexps) (bareExp vexp)+  (bareExp body) NoInfo loc+bareExp (Update src slice v loc) =+  Update (bareExp src) (map bareDimIndex slice) (bareExp v) loc+bareExp (RecordUpdate src fs v _ loc) =+  RecordUpdate (bareExp src) fs (bareExp v) NoInfo loc+bareExp (Project field e _ loc) = Project field (bareExp e) NoInfo loc+bareExp (Index arr slice _ loc) =+  Index (bareExp arr) (map bareDimIndex slice) (NoInfo, NoInfo) loc+bareExp (Unsafe e loc) = Unsafe (bareExp e) loc+bareExp (Assert e1 e2 _ loc) = Assert (bareExp e1) (bareExp e2) NoInfo loc+bareExp (Lambda params body ret _ loc) =+  Lambda (map barePat params) (bareExp body) ret NoInfo loc+bareExp (OpSection name _ loc) = OpSection name NoInfo loc+bareExp (OpSectionLeft name _ arg _ _ loc) =+  OpSectionLeft name NoInfo (bareExp arg) (NoInfo, NoInfo) (NoInfo, NoInfo) loc+bareExp (OpSectionRight name _ arg _ _ loc) =+  OpSectionRight name NoInfo (bareExp arg) (NoInfo, NoInfo) NoInfo loc+bareExp (ProjectSection fields _ loc) = ProjectSection fields NoInfo loc+bareExp (IndexSection slice _ loc) =+  IndexSection (map bareDimIndex slice) NoInfo loc+bareExp (DoLoop _ mergepat mergeexp form loopbody _ loc) =+  DoLoop [] (barePat mergepat) (bareExp mergeexp) (bareLoopForm form)+  (bareExp loopbody) NoInfo loc+bareExp (Constr name es _ loc) =+  Constr name (map bareExp es) NoInfo loc+bareExp (Match e cases _ loc) =+  Match (bareExp e) (fmap bareCase cases) (NoInfo,NoInfo) loc
src/Language/Futhark/TypeChecker.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} -- | The type checker checks whether the program is type-consistent -- and adds type annotations and various other elaborations.  The -- program does not need to have any particular properties for the@@ -23,6 +24,7 @@ import Data.Either import Data.Ord import qualified Data.Map.Strict as M+import qualified Data.Set as S  import Prelude hiding (abs, mod) @@ -33,6 +35,7 @@ import Language.Futhark.TypeChecker.Modules import Language.Futhark.TypeChecker.Terms import Language.Futhark.TypeChecker.Types+import Futhark.Util.Pretty hiding (space)  --- The main checker @@ -126,10 +129,10 @@  dupDefinitionError :: MonadTypeChecker m =>                       Namespace -> Name -> SrcLoc -> SrcLoc -> m a-dupDefinitionError space name pos1 pos2 =-  throwError $ TypeError pos1 $-  "Duplicate definition of " ++ ppSpace space ++ " " ++-  nameToString name ++ ".  Previously defined at " ++ locStr pos2+dupDefinitionError space name loc1 loc2 =+  typeError loc1 mempty $+  "Duplicate definition of" <+> ppr space <+>+  pprName name <> ".  Previously defined at" <+> text (locStr loc2) <> "."  checkForDuplicateDecs :: [DecBase NoInfo Name] -> TypeM () checkForDuplicateDecs =@@ -188,14 +191,19 @@     name' <- checkName Term name loc     (tparams', vtype') <-       checkTypeParams tparams $ \tparams' -> bindingTypeParams tparams' $ do-        (vtype', _) <- checkTypeDecl tparams' vtype+        (vtype', _) <- checkTypeDecl vtype         return (tparams', vtype')      when (emptyDimParam $ unInfo $ expandedType vtype') $-      throwError $ TypeError loc $-      "All function parameters must have non-anonymous sizes.\n" ++-      "Hint: add size parameters to " ++ quote (prettyName name') ++ "."+      typeError loc mempty $+      "All function parameters must have non-anonymous sizes." </>+      "Hint: add size parameters to" <+> pquote (pprName name') <> "." +    let (params, _) = unfoldFunType $ unInfo $ expandedType vtype'+    when (null params && any isSizeParam tparams) $+      typeError loc mempty+      "Size parameters are only allowed on bindings that also have value parameters."+     let binding = BoundV tparams' $ unInfo $ expandedType vtype'         valenv =           mempty { envVtable = M.singleton name' binding@@ -273,7 +281,7 @@ checkSigExp (SigWith s (TypeRef tname ps td trloc) loc) = do   (s_abs, s_env, s') <- checkSigExpToEnv s   checkTypeParams ps $ \ps' -> do-    (td', _) <- bindingTypeParams ps' $ checkTypeDecl ps' td+    (td', _) <- bindingTypeParams ps' $ checkTypeDecl td     (tname', s_abs', s_env') <- refineEnv loc s_abs s_env tname ps' $ unInfo $ expandedType td'     return (MTy s_abs' $ ModEnv s_env', SigWith s' (TypeRef tname' ps' td' trloc) loc) checkSigExp (SigArrow maybe_pname e1 e2 loc) = do@@ -322,7 +330,7 @@   (v', env) <- lookupMod loc v   when (baseName (qualLeaf v') == nameFromString "intrinsics" &&         baseTag (qualLeaf v') <= maxIntrinsicTag) $-    throwError $ TypeError loc "The 'intrinsics' module may not be used in module expressions."+    typeError loc mempty "The 'intrinsics' module may not be used in module expressions."   return (MTy mempty env, ModVar v' loc) checkOneModExp (ModImport name NoInfo loc) = do   (name', env) <- lookupImport loc name@@ -336,7 +344,7 @@       (mty, psubsts, rsubsts) <- applyFunctor loc functor e_mty       return (mty, ModApply f' e' (Info psubsts) (Info rsubsts) loc)     _ ->-      throwError $ TypeError loc "Cannot apply non-parametric module."+      typeError loc mempty "Cannot apply non-parametric module." checkOneModExp (ModAscript me se NoInfo loc) = do   (me_mod, me') <- checkOneModExp me   (se_mty, se') <- checkSigExp se@@ -446,26 +454,32 @@               -> TypeM (Env, TypeBindBase Info VName) checkTypeBind (TypeBind name l tps td doc loc) =   checkTypeParams tps $ \tps' -> do-    (td', l') <- bindingTypeParams tps' $ checkTypeDecl tps' td+    (td', l') <- bindingTypeParams tps' $ checkTypeDecl td +    let used_dims = typeDimNames $ unInfo $ expandedType td'+    case filter ((`S.notMember` used_dims) . typeParamName) $+         filter isSizeParam tps' of+      [] -> return ()+      tp:_ -> typeError loc mempty $+              "Size parameter" <+> pquote (ppr tp) <+> "unused."+     case (l, l') of       (_, Lifted)         | l < Lifted ->-          throwError $ TypeError loc $-          "Non-lifted type abbreviations may not contain functions.\n" +++          typeError loc mempty $+          "Non-lifted type abbreviations may not contain functions." </>           "Hint: consider using 'type^'."       (_, SizeLifted)         | l < SizeLifted ->-          throwError $ TypeError loc $-          "Non-size-lifted type abbreviations may not contain size-lifted types.\n" +++          typeError loc mempty $+          "Non-size-lifted type abbreviations may not contain size-lifted types." </>           "Hint: consider using 'type~'."       (Unlifted, _)         | emptyDimParam $ unInfo $ expandedType td' ->-            warn loc $-            "Non-lifted type abbreviations may not use anonymous sizes in their definition.\n" ++-            "This will become an error in a future version of the compiler!\n" ++-            "Hint: use 'type~' or add size parameters to " ++-            quote (prettyName name) ++ "."+            typeError loc mempty $+            "Non-lifted type abbreviations may not use anonymous sizes in their definition." </>+            "Hint: use 'type~' or add size parameters to" <+>+            pquote (pprName name) <> "."       _ -> return ()      bindSpaced [(Type, name)] $ do@@ -479,7 +493,7 @@  checkValBind :: ValBindBase NoInfo Name -> TypeM (Env, ValBind) checkValBind (ValBind entry fname maybe_tdecl NoInfo tparams params body doc loc) = do-  (fname', tparams', params', maybe_tdecl', rettype, body') <-+  (fname', tparams', params', maybe_tdecl', rettype, retext, body') <-     checkFunDef (fname, maybe_tdecl, tparams, params, body, loc)    let (rettype_params, rettype') = unfoldFunType rettype@@ -488,20 +502,29 @@   case entry' of     Just _       | any isTypeParam tparams' ->-          throwError $ TypeError loc "Entry point functions may not be polymorphic."+          typeError loc mempty "Entry point functions may not be polymorphic." -      | any (not . patternOrderZero) params'-        || any (not . orderZero) rettype_params+      | not (all patternOrderZero params')+        || not (all orderZero rettype_params)         || not (orderZero rettype') ->-          throwError $ TypeError loc "Entry point functions may not be higher-order."+          typeError loc mempty "Entry point functions may not be higher-order." +      | sizes_only_in_ret <-+          S.fromList (map typeParamName tparams') `S.intersection`+          typeDimNames rettype' `S.difference`+          foldMap typeDimNames (map patternStructType params' ++ rettype_params),+        not $ S.null sizes_only_in_ret ->+          typeError loc mempty "Entry point functions must not be size-polymorphic in their return type."+       | p : _ <- filter nastyParameter params' ->-          warn loc $ "Entry point parameter\n\n  " <>-          pretty p <> "\n\nwill have an opaque type, so the entry point will likely not be callable."+          warn loc $ pretty $ "Entry point parameter\n" </>+          indent 2 (ppr p) </>+          "\nwill have an opaque type, so the entry point will likely not be callable."        | nastyReturnType maybe_tdecl' rettype ->-          warn loc $ "Entry point return type\n\n  " <>-          pretty rettype <> "\n\nwill have an opaque type, so the result will likely not be usable."+          warn loc $ pretty $ "Entry point return type\n" </>+          indent 2 (ppr rettype) </>+          "\nwill have an opaque type, so the result will likely not be usable."      _ -> return () @@ -512,7 +535,7 @@                  , envNameMap =                      M.singleton (Term, fname) $ qualName fname'                  },-           ValBind entry' fname' maybe_tdecl' (Info rettype) tparams' params' body' doc loc)+           ValBind entry' fname' maybe_tdecl' (Info (rettype, retext)) tparams' params' body' doc loc)  nastyType :: Monoid als => TypeBase dim als -> Bool nastyType (Scalar Prim{}) = False@@ -568,8 +591,8 @@  checkOneDec (ImportDec name NoInfo loc) = do   (name', env) <- lookupImport loc name-  when ("/futlib" `isPrefixOf` name) $-    warn loc $ name ++ " is already implicitly imported."+  when ("/prelude" `isPrefixOf` name) $+    typeError loc mempty $ ppr name <+> "may not be explicitly imported."   return (mempty, env, ImportDec name (Info name') loc)  checkOneDec (ValDec vb) = do
src/Language/Futhark/TypeChecker/Modules.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE TupleSections #-}+{-# LANGUAGE OverloadedStrings #-} module Language.Futhark.TypeChecker.Modules   ( matchMTys   , newNamesForMTy@@ -181,13 +182,12 @@                               TypeSub $ TypeAbbr l cur_ps t),                               (v, TypeSub $ TypeAbbr l ps t)])                 env)-      else throwError $ TypeError loc $ "Cannot refine a type having " <>+      else typeError loc mempty $ "Cannot refine a type having" <+>            tpMsg ps <> " with a type having " <> tpMsg cur_ps <> "."   | otherwise =-      throwError $ TypeError loc $-      pretty tname ++ " is not an abstract type in the module type."+      typeError loc mempty $ ppr tname <+> "is not an abstract type in the module type."   where tpMsg [] = "no type parameters"-        tpMsg xs = "type parameters " <> unwords (map pretty xs)+        tpMsg xs = "type parameters" <+> spread (map ppr xs)  paramsMatch :: [TypeParam] -> [TypeParam] -> Bool paramsMatch ps1 ps2 = length ps1 == length ps2 && all match (zip ps1 ps2)@@ -222,7 +222,11 @@     case findTypeDef (fmap baseName name) mod of       Just (name', TypeAbbr mod_l ps t)         | mod_l > name_l ->-            mismatchedLiftedness name_l (map qualLeaf $ M.keys mod_abs)+            mismatchedLiftedness name_l+            (map qualLeaf $ M.keys mod_abs) (qualLeaf name) (mod_l, ps, t)+        | name_l < SizeLifted,+          emptyDims t ->+            anonymousSizes (map qualLeaf $ M.keys mod_abs)             (qualLeaf name) (mod_l, ps, t)         | Just (abs_name, _) <- M.lookup (fmap baseName name) abs_mapping ->             return (qualLeaf name, (abs_name, TypeAbbr name_l ps t))@@ -231,14 +235,25 @@       _ ->         missingType loc $ fmap baseName name   where mismatchedLiftedness name_l abs name mod_t =-          Left $ TypeError loc $-          unlines ["Module defines",-                   sindent $ ppTypeAbbr abs name mod_t,-                   "but module type requires " ++ what ++ "."]+          Left $ TypeError loc mempty $+          "Module defines" </>+          indent 2 (ppTypeAbbr abs name mod_t) </>+          "but module type requires" <+> text what <> "."           where what = case name_l of Unlifted -> "a non-lifted type"                                       SizeLifted -> "a size-lifted type"                                       Lifted -> "a lifted type" +        anonymousSizes abs name mod_t =+          Left $ TypeError loc mempty $+          "Module defines" </>+          indent 2 (ppTypeAbbr abs name mod_t) </>+          "which contains anonymous sizes, but module type requires non-lifted type."++        emptyDims :: StructType -> Bool+        emptyDims = isNothing . traverseDims onDim+          where onDim PosImmediate AnyDim = Nothing+                onDim _ d = Just d+ resolveMTyNames :: MTy -> MTy                 -> M.Map VName (QualName VName) resolveMTyNames = resolveMTyNames'@@ -260,7 +275,7 @@                   Just (QualName _ modname')                     | Just sig_env_mod <-                         M.lookup modname' $ envModTable mod_env ->-                      resolveModNames mod_env_mod sig_env_mod+                      resolveModNames sig_env_mod mod_env_mod                   _ -> mempty           in mconcat [ resolve Term mod_env $ envVtable sig_env                      , resolve Type mod_env $ envVtable sig_env@@ -275,18 +290,18 @@  missingType :: Pretty a => SrcLoc -> a -> Either TypeError b missingType loc name =-  Left $ TypeError loc $-  "Module does not define a type named " ++ pretty name ++ "."+  Left $ TypeError loc mempty $+  "Module does not define a type named" <+> ppr name <> "."  missingVal :: Pretty a => SrcLoc -> a -> Either TypeError b missingVal loc name =-  Left $ TypeError loc $-  "Module does not define a value named " ++ pretty name ++ "."+  Left $ TypeError loc mempty $+  "Module does not define a value named" <+> ppr name <> "."  missingMod :: Pretty a => SrcLoc -> a -> Either TypeError b missingMod loc name =-  Left $ TypeError loc $-  "Module does not define a module named " ++ pretty name ++ "."+  Left $ TypeError loc mempty $+  "Module does not define a module named" <+> ppr name <> "."  mismatchedType :: SrcLoc                -> [VName]@@ -295,23 +310,20 @@                -> (Liftedness, [TypeParam], StructType)                -> Either TypeError b mismatchedType loc abs name spec_t env_t =-  Left $ TypeError loc $-  unlines ["Module defines",-           sindent $ ppTypeAbbr abs name env_t,-           "but module type requires",-           sindent $ ppTypeAbbr abs name spec_t]--sindent :: String -> String-sindent = intercalate "\n" . map ("  "++) . lines+  Left $ TypeError loc mempty $+  "Module defines" </>+  indent 2 (ppTypeAbbr abs name env_t) </>+  "but module type requires" </>+  indent 2 (ppTypeAbbr abs name spec_t) -ppTypeAbbr :: [VName] -> VName -> (Liftedness, [TypeParam], StructType) -> String+ppTypeAbbr :: [VName] -> VName -> (Liftedness, [TypeParam], StructType) -> Doc ppTypeAbbr abs name (l, ps, Scalar (TypeVar () _ tn args))   | typeLeaf tn `elem` abs,     map typeParamToArg ps == args =-      pretty $ text "type" <> ppr l <+> pprName name <+>+      "type" <> ppr l <+> pprName name <+>       spread (map ppr ps) ppTypeAbbr _ name (l, ps, t) =-  pretty $ text "type" <> ppr l <+> pprName name <+>+  "type" <> ppr l <+> pprName name <+>   spread (map ppr ps) <+> equals <+/>   nest 2 (align (ppr t)) @@ -330,10 +342,12 @@                -> Either TypeError (M.Map VName VName)      matchMTys' _ (MTy _ ModFun{}) (MTy _ ModEnv{}) loc =-      Left $ TypeError loc "Cannot match parametric module with non-parametric module type."+      Left $ TypeError loc mempty+      "Cannot match parametric module with non-parametric module type."      matchMTys' _ (MTy _ ModEnv{}) (MTy _ ModFun{}) loc =-      Left $ TypeError loc "Cannot match non-parametric module with paramatric module type."+      Left $ TypeError loc mempty+      "Cannot match non-parametric module with paramatric module type."      matchMTys' old_abs_subst_to_type (MTy mod_abs mod) (MTy sig_abs sig) loc = do       -- Check that abstract types in 'sig' have an implementation in@@ -350,9 +364,11 @@     matchMods :: TypeSubs -> Mod -> Mod -> SrcLoc               -> Either TypeError (M.Map VName VName)     matchMods _ ModEnv{} ModFun{} loc =-      Left $ TypeError loc "Cannot match non-parametric module with parametric module type."+      Left $ TypeError loc mempty+      "Cannot match non-parametric module with parametric module type."     matchMods _ ModFun{} ModEnv{} loc =-      Left $ TypeError loc "Cannot match parametric module with non-parametric module type."+      Left $ TypeError loc mempty+      "Cannot match parametric module with non-parametric module type."      matchMods abs_subst_to_type (ModEnv mod) (ModEnv sig) loc =       matchEnvs abs_subst_to_type mod sig loc@@ -414,6 +430,15 @@       -- We have to create substitutions for the type parameters, too.       unless (length spec_ps == length ps) $ nomatch spec_t       param_substs <- mconcat <$> zipWithM matchTypeParam spec_ps ps++      case S.toList (mustBeExplicitInType t) `intersect` map typeParamName ps of+        [] -> return ()+        d:_ -> Left $ TypeError loc mempty $+               "Type" </>+               indent 2 (ppTypeAbbr [] name (l, ps, t)) </>+               textwrap "cannot be made abstract because size parameter" <+/> pquote (pprName d) <+/>+               textwrap "is not used as an array size in the definition."+       let spec_t' = substituteTypes (param_substs<>abs_subst_to_type) spec_t       if spec_t' == t         then return (spec_name, name)@@ -440,24 +465,26 @@       case matchValBinding loc spec_v v of         Nothing -> return (spec_name, name)         Just problem ->-          Left $ TypeError loc $ pretty $-          text "Module type specifies" </>+          Left $ TypeError loc mempty $+          "Module type specifies" </>           indent 2 (ppValBind spec_name spec_v) </>-          text "but module provides" </>+          "but module provides" </>           indent 2 (ppValBind spec_name v) </>-          maybe mempty text problem+          fromMaybe mempty problem -    matchValBinding :: SrcLoc -> BoundV -> BoundV -> Maybe (Maybe String)+    matchValBinding :: SrcLoc -> BoundV -> BoundV -> Maybe (Maybe Doc)     matchValBinding loc (BoundV _ orig_spec_t) (BoundV tps orig_t) =-      case doUnification loc tps (removeShapeAnnotations orig_spec_t) (removeShapeAnnotations orig_t) of-        Left (TypeError _ err) -> Just $ Just err+      case doUnification loc tps (toStruct orig_spec_t) (toStruct orig_t) of+        Left (TypeError _ notes msg) ->+          Just $ Just $ msg <> ppr notes         -- Even if they unify, we still have to verify the uniqueness         -- properties.-        Right t | t `subtypeOf` removeShapeAnnotations orig_spec_t -> Nothing+        Right t | noSizes t `subtypeOf`+                  noSizes orig_spec_t -> Nothing                 | otherwise -> Just Nothing      ppValBind v (BoundV tps t) =-      text "val" <+> pprName v <+> spread (map ppr tps) <+> colon </>+      "val" <+> pprName v <+> spread (map ppr tps) <+> colon </>       indent 2 (align (ppr t))  applyFunctor :: SrcLoc -> FunSig -> MTy
src/Language/Futhark/TypeChecker/Monad.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts, TupleSections #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE OverloadedStrings #-} -- | Main monad in which the type checker runs, as well as ancillary -- data definitions. module Language.Futhark.TypeChecker.Monad@@ -20,10 +23,8 @@   , unknownVariableError   , underscoreUse   , functionIsNotValue--  , BreadCrumb(..)-  , MonadBreadCrumbs(..)-  , typeError+  , Notes+  , aNote    , MonadTypeChecker(..)   , checkName@@ -51,7 +52,6 @@   , Namespace(..)   , intrinsicsNameMap   , topLevelNameMap-  , ppSpace   ) where @@ -76,53 +76,68 @@ import Language.Futhark.Warnings import Futhark.FreshNames hiding (newName) import qualified Futhark.FreshNames+import Futhark.Util.Pretty hiding (space) --- | Information about an error during type checking.  The 'Show'--- instance for this type produces a human-readable description.-data TypeError = TypeError SrcLoc String+-- | A note with extra information regarding a type error.+newtype Note = Note Doc -unexpectedType :: MonadTypeChecker m => SrcLoc -> TypeBase () () -> [TypeBase () ()] -> m a+newtype Notes = Notes [Note]+  deriving (Semigroup, Monoid)++instance Pretty Note where+  ppr (Note msg) = "Note:" <+> align msg++instance Pretty Notes where+  ppr (Notes notes) = foldMap (((line<>line)<>) . ppr) notes++aNote :: Pretty a => a -> Notes+aNote = Notes . pure . Note . ppr++-- | Information about an error during type checking.+data TypeError = TypeError SrcLoc Notes Doc++instance Pretty TypeError where+  ppr (TypeError loc notes msg) =+    "Error at" <+> text (locStr loc) <+> ":" </>+    msg <> ppr notes++unexpectedType :: MonadTypeChecker m => SrcLoc -> StructType -> [StructType] -> m a unexpectedType loc _ [] =-  throwError $ TypeError loc $-  "Type of expression at " ++ locStr loc +++  typeError loc mempty $+  "Type of expression at" <+> text (locStr loc) <+>   "cannot have any type - possibly a bug in the type checker." unexpectedType loc t ts =-  throwError $ TypeError loc $-  "Type of expression at " ++ locStr loc ++ " must be one of " ++-  intercalate ", " (map pretty ts) ++ ", but is " ++-  pretty t ++ "."+  typeError loc mempty $+  "Type of expression at" <+> text (locStr loc) <+> "must be one of" <+>+  commasep (map ppr ts) <> ", but is" <+>+  ppr t <> "."  undefinedType :: MonadTypeChecker m => SrcLoc -> QualName Name -> m a undefinedType loc name =-  throwError $ TypeError loc $-  "Unknown type " ++ pretty name ++ "."+  typeError loc mempty $ "Unknown type" <+> ppr name <> "."  functionIsNotValue :: MonadTypeChecker m => SrcLoc -> QualName Name -> m a functionIsNotValue loc name =-  throwError $ TypeError loc $-  "Attempt to use function " ++ pretty name ++ " as value at " ++ locStr loc ++ "."+  typeError loc mempty $+  "Attempt to use function" <+> ppr name <+> "as value at" <+>+  text (locStr loc) <> "."  unappliedFunctor :: MonadTypeChecker m => SrcLoc -> m a unappliedFunctor loc =-  throwError $ TypeError loc "Cannot have parametric module here."+  typeError loc mempty "Cannot have parametric module here."  unknownVariableError :: MonadTypeChecker m =>                         Namespace -> QualName Name -> SrcLoc -> m a unknownVariableError space name loc =-  throwError $ TypeError loc $-  "Unknown " ++ ppSpace space ++ " " ++ quote (pretty name)+  typeError loc mempty $+  "Unknown" <+> ppr space <+> pquote (ppr name)  underscoreUse :: MonadTypeChecker m =>                  SrcLoc -> QualName Name -> m a underscoreUse loc name =-  throwError $ TypeError loc $-  "Use of " ++ quote (pretty name) +++  typeError loc mempty $ "Use of" <+> pquote (ppr name) <>   ": variables prefixed with underscore may not be accessed." -instance Show TypeError where-  show (TypeError pos msg) =-    "Error at " ++ locStr pos ++ ":\n" ++ msg- type ImportTable = M.Map String Env  data Context = Context { contextEnv :: Env@@ -132,7 +147,7 @@  -- | The type checker runs in this monad. newtype TypeM a = TypeM (RWST-                         Context -- Reader+                         Context            -- Reader                          Warnings           -- Writer                          VNameSource        -- State                          (Except TypeError) -- Inner monad@@ -169,9 +184,9 @@   my_path <- asks contextImportName   let canonical_import = includeToString $ mkImportFrom my_path file loc   case M.lookup canonical_import imports of-    Nothing    -> throwError $ TypeError loc $-                  unlines ["Unknown import \"" ++ canonical_import ++ "\"",-                           "Known: " ++ intercalate ", " (M.keys imports)]+    Nothing    -> typeError loc mempty $+                  "Unknown import" <+> dquotes (text canonical_import) </>+                  "Known:" <+> commasep (map text (M.keys imports))     Just scope -> return (canonical_import, scope)  localEnv :: Env -> TypeM a -> TypeM a@@ -179,37 +194,7 @@   let env' = env <> contextEnv ctx   in ctx { contextEnv = env' } --- | A piece of information that describes what process the type--- checker currently performing.  This is used to give better error--- messages.-data BreadCrumb = MatchingTypes (TypeBase () ()) (TypeBase () ())-                | MatchingFields Name--instance Show BreadCrumb where-  show (MatchingTypes t1 t2) =-    "When matching type\n" ++ indent (pretty t1) ++-    "\nwith\n" ++ indent (pretty t2)-    where indent = intercalate "\n" . map ("  "++) . lines-  show (MatchingFields field) =-    "When matching types of record field " ++ quote (pretty field) ++ "."---- | Tracking breadcrumbs to give a kind of "stack trace" in errors.-class Monad m => MonadBreadCrumbs m where-  breadCrumb :: BreadCrumb -> m a -> m a-  breadCrumb _ m = m--  getBreadCrumbs :: m [BreadCrumb]-  getBreadCrumbs = return []--typeError :: (Located loc, MonadError TypeError m, MonadBreadCrumbs m) =>-             loc -> String -> m a-typeError loc s = do-  bc <- getBreadCrumbs-  let bc' | null bc = ""-          | otherwise = "\n" ++ unlines (map show bc)-  throwError $ TypeError (srclocOf loc) $ s ++ bc'--class MonadError TypeError m => MonadTypeChecker m where+class Monad m => MonadTypeChecker m where   warn :: Located loc => loc -> String -> m ()    newName :: VName -> m VName@@ -229,9 +214,11 @@     (v', t) <- lookupVar loc v     case t of       Scalar (Prim (Signed Int32)) -> return v'-      _ -> throwError $ TypeError loc $-           "Dimension declaration " ++ pretty v ++ " should be of type `i32`."+      _ -> typeError loc mempty $+           "Dimension declaration" <+> ppr v <+> "should be of type i32." +  typeError :: Located loc => loc -> Notes -> Doc -> m a+ checkName :: MonadTypeChecker m => Namespace -> Name -> SrcLoc -> m VName checkName space name loc = qualLeaf <$> checkQualName space (qualName name) loc @@ -283,11 +270,13 @@         | "_" `isPrefixOf` baseString name -> underscoreUse loc qn         | otherwise ->             case getType t of-              Left{} -> throwError $ TypeError loc $-                        "Attempt to use function " ++ baseString name ++ " as value."+              Left{} -> typeError loc mempty $+                        "Attempt to use function" <+> pprName name <+> "as value."               Right t' -> return (qn', fromStruct $                                        qualifyTypeVars outer_env mempty qs t') +  typeError loc notes s = throwError $ TypeError (srclocOf loc) notes s+ -- | Extract from a type either a function type comprising a list of -- parameter types and a return type, or a first-order type. getType :: TypeBase dim as@@ -343,7 +332,8 @@                           []       -> QualName (qs++orig_qs) name          reachable [] name env =-          isJust $ find matches $ M.elems (envTypeTable env)+          name `M.member` envVtable env ||+          isJust (find matches $ M.elems (envTypeTable env))           where matches (TypeAbbr _ _ (Scalar (TypeVar _ _ (TypeName x_qs name') _))) =                   null x_qs && name == name'                 matches _ = False@@ -353,7 +343,7 @@               reachable qs' name env'           | otherwise = False -badOnLeft :: MonadTypeChecker m => Either TypeError a -> m a+badOnLeft :: Either TypeError a -> TypeM a badOnLeft = either throwError return  anySignedType :: [PrimType]@@ -375,11 +365,6 @@ anyPrimType = Bool : anyIntType ++ anyFloatType  --- Name handling--ppSpace :: Namespace -> String-ppSpace Term = "name"-ppSpace Type = "type"-ppSpace Signature = "module type"  intrinsicsNameMap :: NameMap intrinsicsNameMap = M.fromList $ map mapping $ M.toList intrinsics
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -1,2130 +1,2956 @@ {-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts #-} {-# LANGUAGE FlexibleInstances, DeriveFunctor #-} {-# Language TupleSections #-}--- | Facilities for type-checking Futhark terms.  Checking a term--- requires a little more context to track uniqueness and such.------ Type inference is implemented through a variation of--- Hindley-Milner.  The main complication is supporting the rich--- number of built-in language constructs, as well as uniqueness--- types.  This is mostly done in an ad hoc way, and many programs--- will require the programmer to fall back on type annotations.-module Language.Futhark.TypeChecker.Terms-  ( checkOneExp-  , checkFunDef-  )-where--import Control.Monad.Except-import Control.Monad.State-import Control.Monad.RWS hiding (Sum)-import qualified Control.Monad.Fail as Fail-import Data.Char (isAlpha)-import Data.List-import qualified Data.List.NonEmpty as NE-import Data.Loc-import Data.Maybe-import qualified Data.Map.Strict as M-import qualified Data.Set as S--import Prelude hiding (mod)--import Language.Futhark-import Language.Futhark.Semantic (includeToString)-import Language.Futhark.Traversals-import Language.Futhark.TypeChecker.Monad hiding (BoundV, checkQualNameWithEnv)-import Language.Futhark.TypeChecker.Types hiding (checkTypeDecl)-import Language.Futhark.TypeChecker.Unify hiding (Usage)-import qualified Language.Futhark.TypeChecker.Types as Types-import qualified Language.Futhark.TypeChecker.Monad as TypeM-import Futhark.Util.Pretty hiding (space, bool, group)----- Uniqueness--data Usage = Consumed SrcLoc-           | Observed SrcLoc-           deriving (Eq, Ord, Show)--type Names = S.Set VName---- | The consumption set is a Maybe so we can distinguish whether a--- consumption took place, but the variable went out of scope since,--- or no consumption at all took place.-data Occurence = Occurence { observed :: Names-                           , consumed :: Maybe Names-                           , location :: SrcLoc-                           }-             deriving (Eq, Show)--instance Located Occurence where-  locOf = locOf . location--observation :: Aliasing -> SrcLoc -> Occurence-observation = flip Occurence Nothing . S.map aliasVar--consumption :: Aliasing -> SrcLoc -> Occurence-consumption = Occurence S.empty . Just . S.map aliasVar---- | A null occurence is one that we can remove without affecting--- anything.-nullOccurence :: Occurence -> Bool-nullOccurence occ = S.null (observed occ) && isNothing (consumed occ)---- | A seminull occurence is one that does not contain references to--- any variables in scope.  The big difference is that a seminull--- occurence may denote a consumption, as long as the array that was--- consumed is now out of scope.-seminullOccurence :: Occurence -> Bool-seminullOccurence occ = S.null (observed occ) && maybe True S.null (consumed occ)--type Occurences = [Occurence]--type UsageMap = M.Map VName [Usage]--usageMap :: Occurences -> UsageMap-usageMap = foldl comb M.empty-  where comb m (Occurence obs cons loc) =-          let m' = S.foldl' (ins $ Observed loc) m obs-          in S.foldl' (ins $ Consumed loc) m' $ fromMaybe mempty cons-        ins v m k = M.insertWith (++) k [v] m--combineOccurences :: MonadTypeChecker m => VName -> Usage -> Usage -> m Usage-combineOccurences _ (Observed loc) (Observed _) = return $ Observed loc-combineOccurences name (Consumed wloc) (Observed rloc) =-  useAfterConsume (baseName name) rloc wloc-combineOccurences name (Observed rloc) (Consumed wloc) =-  useAfterConsume (baseName name) rloc wloc-combineOccurences name (Consumed loc1) (Consumed loc2) =-  consumeAfterConsume (baseName name) (max loc1 loc2) (min loc1 loc2)--checkOccurences :: MonadTypeChecker m => Occurences -> m ()-checkOccurences = void . M.traverseWithKey comb . usageMap-  where comb _    []     = return ()-        comb name (u:us) = foldM_ (combineOccurences name) u us--allObserved :: Occurences -> Names-allObserved = S.unions . map observed--allConsumed :: Occurences -> Names-allConsumed = S.unions . map (fromMaybe mempty . consumed)--allOccuring :: Occurences -> Names-allOccuring occs = allConsumed occs <> allObserved occs--anyConsumption :: Occurences -> Maybe Occurence-anyConsumption = find (isJust . consumed)--seqOccurences :: Occurences -> Occurences -> Occurences-seqOccurences occurs1 occurs2 =-  filter (not . nullOccurence) $ map filt occurs1 ++ occurs2-  where filt occ =-          occ { observed = observed occ `S.difference` postcons }-        postcons = allConsumed occurs2--altOccurences :: Occurences -> Occurences -> Occurences-altOccurences occurs1 occurs2 =-  filter (not . nullOccurence) $ map filt1 occurs1 ++ map filt2 occurs2-  where filt1 occ =-          occ { consumed = S.difference <$> consumed occ <*> pure cons2-              , observed = observed occ `S.difference` cons2 }-        filt2 occ =-          occ { consumed = consumed occ-              , observed = observed occ `S.difference` cons1 }-        cons1 = allConsumed occurs1-        cons2 = allConsumed occurs2----- Scope management---- | Whether something is a global or a local variable.-data Locality = Local | Global-              deriving (Show)--data ValBinding = BoundV Locality [TypeParam] PatternType-                -- ^ Aliases in parameters indicate the lexical-                -- closure.-                | OverloadedF [PrimType] [Maybe PrimType] (Maybe PrimType)-                | EqualityF-                | WasConsumed SrcLoc-                deriving (Show)---- | Type checking happens with access to this environment.  The--- 'TermScope' will be extended during type-checking as bindings come into--- scope.-data TermEnv = TermEnv { termScope :: TermScope-                       , termBreadCrumbs :: [BreadCrumb]-                         -- ^ Most recent first.-                       , termLevel :: Level-                       }--data TermScope = TermScope { scopeVtable  :: M.Map VName ValBinding-                           , scopeTypeTable :: M.Map VName TypeBinding-                           , scopeModTable :: M.Map VName Mod-                           , scopeNameMap :: NameMap-                           } deriving (Show)--instance Semigroup TermScope where-  TermScope vt1 tt1 mt1 nt1 <> TermScope vt2 tt2 mt2 nt2 =-    TermScope (vt2 `M.union` vt1) (tt2 `M.union` tt1) (mt1 `M.union` mt2) (nt2 `M.union` nt1)--envToTermScope :: Env -> TermScope-envToTermScope env = TermScope { scopeVtable = vtable-                               , scopeTypeTable = envTypeTable env-                               , scopeNameMap = envNameMap env-                               , scopeModTable = envModTable env-                               }-  where vtable = M.mapWithKey valBinding $ envVtable env-        valBinding k (TypeM.BoundV tps v) =-          BoundV Global tps $ v `setAliases`-          (if arrayRank v > 0 then S.singleton (AliasBound k) else mempty)--withEnv :: TermEnv -> Env -> TermEnv-withEnv tenv env = tenv { termScope = termScope tenv <> envToTermScope env }--overloadedTypeVars :: Constraints -> Names-overloadedTypeVars = mconcat . map f . M.elems-  where f (_, HasFields fs _) = mconcat $ map typeVars $ M.elems fs-        f _ = mempty---- | Get the type of an expression, with all type variables--- substituted.  Never call 'typeOf' directly (except in a few--- carefully inspected locations)!-expType :: Exp -> TermTypeM PatternType-expType = normaliseType . typeOf---- | The state is a set of constraints and a counter for generating--- type names.  This is distinct from the usual counter we use for--- generating unique names, as these will be user-visible.-type TermTypeState = (Constraints, Int)--newtype TermTypeM a = TermTypeM (RWST-                                 TermEnv-                                 Occurences-                                 TermTypeState-                                 TypeM-                                 a)-  deriving (Monad, Functor, Applicative,-            MonadReader TermEnv,-            MonadWriter Occurences,-            MonadState TermTypeState,-            MonadError TypeError)--instance Fail.MonadFail TermTypeM where-  fail = typeError (noLoc :: SrcLoc) . ("unknown failure (likely a bug): "++)--instance MonadUnify TermTypeM where-  getConstraints = gets fst-  putConstraints x = modify $ \s -> (x, snd s)--  newTypeVar loc desc = do-    i <- incCounter-    v <- newID $ mkTypeVarName desc i-    constrain v $ NoConstraint Lifted $ mkUsage' loc-    return $ Scalar $ TypeVar mempty Nonunique (typeName v) []--  curLevel = asks termLevel--instance MonadBreadCrumbs TermTypeM where-  breadCrumb bc = local $ \env ->-    env { termBreadCrumbs = bc : termBreadCrumbs env }-  getBreadCrumbs = asks termBreadCrumbs--runTermTypeM :: TermTypeM a -> TypeM (a, Occurences)-runTermTypeM (TermTypeM m) = do-  initial_scope <- (initialTermScope <>) . envToTermScope <$> askEnv-  let initial_tenv = TermEnv { termScope = initial_scope-                             , termBreadCrumbs = mempty-                             , termLevel = 0-                             }-  evalRWST m initial_tenv (mempty, 0)--liftTypeM :: TypeM a -> TermTypeM a-liftTypeM = TermTypeM . lift--localScope :: (TermScope -> TermScope) -> TermTypeM a -> TermTypeM a-localScope f = local $ \tenv -> tenv { termScope = f $ termScope tenv }--incCounter :: TermTypeM Int-incCounter = do (x, i) <- get-                put (x, i+1)-                return i--constrain :: VName -> Constraint -> TermTypeM ()-constrain v c = do-  lvl <- curLevel-  modifyConstraints $ M.insert v (lvl, c)--incLevel :: TermTypeM a -> TermTypeM a-incLevel = local $ \env -> env { termLevel = termLevel env + 1 }--initialTermScope :: TermScope-initialTermScope = TermScope { scopeVtable = initialVtable-                             , scopeTypeTable = mempty-                             , scopeNameMap = topLevelNameMap-                             , scopeModTable = mempty-                             }-  where initialVtable = M.fromList $ mapMaybe addIntrinsicF $ M.toList intrinsics--        prim = Scalar . Prim-        arrow x y = Scalar $ Arrow mempty Unnamed x y--        addIntrinsicF (name, IntrinsicMonoFun pts t) =-          Just (name, BoundV Global [] $ arrow pts' $ prim t)-          where pts' = case pts of [pt] -> prim pt-                                   _    -> tupleRecord $ map prim pts--        addIntrinsicF (name, IntrinsicOverloadedFun ts pts rts) =-          Just (name, OverloadedF ts pts rts)-        addIntrinsicF (name, IntrinsicPolyFun tvs pts rt) =-          Just (name, BoundV Global tvs $-                      fromStruct $ anyDimShapeAnnotations $-                      Scalar $ Arrow mempty Unnamed pts' rt)-          where pts' = case pts of [pt] -> pt-                                   _    -> tupleRecord pts-        addIntrinsicF (name, IntrinsicEquality) =-          Just (name, EqualityF)-        addIntrinsicF _ = Nothing--instance MonadTypeChecker TermTypeM where-  warn loc problem = liftTypeM $ warn loc problem-  newName = liftTypeM . newName-  newID = liftTypeM . newID--  checkQualName space name loc = snd <$> checkQualNameWithEnv space name loc--  bindNameMap m = localScope $ \scope ->-    scope { scopeNameMap = m <> scopeNameMap scope }--  bindVal v (TypeM.BoundV tps t) = localScope $ \scope ->-    scope { scopeVtable = M.insert v vb $ scopeVtable scope }-    where vb = BoundV Local tps $ fromStruct t--  lookupType loc qn = do-    outer_env <- liftTypeM askEnv-    (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Type qn loc-    case M.lookup name $ scopeTypeTable scope of-      Nothing -> undefinedType loc qn-      Just (TypeAbbr l ps def) ->-        return (qn', ps, qualifyTypeVars outer_env (map typeParamName ps) qs def, l)--  lookupMod loc qn = do-    (scope, qn'@(QualName _ name)) <- checkQualNameWithEnv Term qn loc-    case M.lookup name $ scopeModTable scope of-      Nothing -> unknownVariableError Term qn loc-      Just m  -> return (qn', m)--  lookupVar loc qn = do-    outer_env <- liftTypeM askEnv-    (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Term qn loc-    let usage = mkUsage loc $ "use of " ++ quote (pretty qn)--    t <- case M.lookup name $ scopeVtable scope of-      Nothing -> throwError $ TypeError loc $-                 "Unknown variable " ++ quote (pretty qn) ++ "."--      Just (WasConsumed wloc) -> useAfterConsume (baseName name) loc wloc--      Just (BoundV _ tparams t)-        | "_" `isPrefixOf` baseString name -> underscoreUse loc qn-        | otherwise -> do-            (tnames, t') <- instantiateTypeScheme loc tparams t-            let qual = qualifyTypeVars outer_env tnames qs-            qual . anyDimShapeAnnotations <$> normaliseType t'--      Just EqualityF -> do-        argtype <- newTypeVar loc "t"-        equalityType usage argtype-        return $-          Scalar $ Arrow mempty Unnamed argtype $-          Scalar $ Arrow mempty Unnamed argtype $ Scalar $ Prim Bool--      Just (OverloadedF ts pts rt) -> do-        argtype <- newTypeVar loc "t"-        mustBeOneOf ts usage argtype-        let (pts', rt') = instOverloaded argtype pts rt-            arrow xt yt = Scalar $ Arrow mempty Unnamed xt yt-        return $ fromStruct $ vacuousShapeAnnotations $ foldr arrow rt' pts'--    observe $ Ident name (Info t) loc-    return (qn', t)--      where instOverloaded argtype pts rt =-              (map (maybe (toStruct argtype) (Scalar . Prim)) pts,-               maybe (toStruct argtype) (Scalar . Prim) rt)--  checkNamedDim loc v = do-    (v', t) <- lookupVar loc v-    unify (mkUsage loc "use as array size") (toStructural t) $-      Scalar $ Prim $ Signed Int32-    return v'--checkQualNameWithEnv :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName)-checkQualNameWithEnv space qn@(QualName quals name) loc = do-  scope <- asks termScope-  descend scope quals-  where descend scope []-          | Just name' <- M.lookup (space, name) $ scopeNameMap scope =-              return (scope, name')-          | otherwise =-              unknownVariableError space qn loc--        descend scope (q:qs)-          | Just (QualName _ q') <- M.lookup (Term, q) $ scopeNameMap scope,-            Just res <- M.lookup q' $ scopeModTable scope =-              case res of-                -- Check if we are referring to the magical intrinsics-                -- module.-                _ | baseTag q' <= maxIntrinsicTag ->-                      checkIntrinsic space qn loc-                ModEnv q_scope -> do-                  (scope', QualName qs' name') <- descend (envToTermScope q_scope) qs-                  return (scope', QualName (q':qs') name')-                ModFun{} -> unappliedFunctor loc-          | otherwise =-              unknownVariableError space qn loc--checkIntrinsic :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName)-checkIntrinsic space qn@(QualName _ name) loc-  | Just v <- M.lookup (space, name) intrinsicsNameMap = do-      me <- liftTypeM askImportName-      unless ("/futlib" `isPrefixOf` includeToString me) $-        warn loc "Using intrinsic functions directly can easily crash the compiler or result in wrong code generation."-      scope <- asks termScope-      return (scope, v)-  | otherwise =-      unknownVariableError space qn loc---- | Wrap 'Types.checkTypeDecl' to also perform an observation of--- every size in the type.-checkTypeDecl :: TypeDeclBase NoInfo Name -> TermTypeM (TypeDeclBase Info VName)-checkTypeDecl tdecl = do-  (tdecl', _) <- Types.checkTypeDecl [] tdecl-  mapM_ observeDim $ nestedDims $ unInfo $ expandedType tdecl'-  return tdecl'-  where observeDim (NamedDim v) =-          observe $ Ident (qualLeaf v) (Info $ Scalar $ Prim $ Signed Int32) noLoc-        observeDim _ = return ()---- | Instantiate a type scheme with fresh type variables for its type--- parameters. Returns the names of the fresh type variables, the instance--- list, and the instantiated type.-instantiateTypeScheme :: SrcLoc -> [TypeParam] -> PatternType-                      -> TermTypeM ([VName], PatternType)-instantiateTypeScheme loc tparams t = do-  let tparams' = filter isTypeParam tparams-      tnames = map typeParamName tparams'-  (fresh_tnames, substs) <- unzip <$> mapM (instantiateTypeParam loc) tparams'-  let substs' = M.fromList $ zip tnames substs-      t' = substTypesAny (`M.lookup` substs') t-  return (fresh_tnames, t')---- | Create a new type name and insert it (unconstrained) in the--- substitution map.-instantiateTypeParam :: Monoid as => SrcLoc -> TypeParam -> TermTypeM (VName, Subst (TypeBase dim as))-instantiateTypeParam loc tparam = do-  i <- incCounter-  v <- newID $ mkTypeVarName (takeWhile isAlpha (baseString (typeParamName tparam))) i-  constrain v $ NoConstraint l $ mkUsage' loc-  return (v, Subst $ Scalar $ TypeVar mempty Nonunique (typeName v) [])-  where l = case tparam of TypeParamType x _ _ -> x-                           _                   -> Lifted--newArrayType :: SrcLoc -> String -> Int -> TermTypeM (TypeBase () (), TypeBase () ())-newArrayType loc desc r = do-  v <- newID $ nameFromString desc-  constrain v $ NoConstraint Unlifted $ mkUsage' loc-  let rowt = TypeVar () Nonunique (typeName v) []-  return (Array () Nonunique rowt (ShapeDecl $ replicate r ()),-          Scalar rowt)----- Errors--funName :: Maybe Name -> String-funName Nothing = "anonymous function"-funName (Just fname) = "function " ++ quote (pretty fname)--useAfterConsume :: MonadTypeChecker m => Name -> SrcLoc -> SrcLoc -> m a-useAfterConsume name rloc wloc =-  throwError $ TypeError rloc $-  "Variable " ++ quote (pretty name) ++ " previously consumed at " ++-  locStr wloc ++ ".  (Possibly through aliasing)"--consumeAfterConsume :: MonadTypeChecker m => Name -> SrcLoc -> SrcLoc -> m a-consumeAfterConsume name loc1 loc2 =-  throwError $ TypeError loc2 $-  "Variable " ++ pretty name ++ " previously consumed at " ++ locStr loc1 ++ "."--badLetWithValue :: MonadTypeChecker m => SrcLoc -> m a-badLetWithValue loc =-  throwError $ TypeError loc-  "New value for elements in let-with shares data with source array.  This is illegal, as it prevents in-place modification."--returnAliased :: MonadTypeChecker m => Maybe Name -> Name -> SrcLoc -> m ()-returnAliased fname name loc =-  throwError $ TypeError loc $-  "Unique return value of " ++ funName fname ++-  " is aliased to " ++ quote (pretty name) ++ ", which is not consumed."--uniqueReturnAliased :: MonadTypeChecker m => Maybe Name -> SrcLoc -> m a-uniqueReturnAliased fname loc =-  throwError $ TypeError loc $-  "A unique tuple element of return value of " ++-  funName fname ++ " is aliased to some other tuple component."----- Basic checking---- | Determine if two types are identical, ignoring uniqueness.--- Causes a 'TypeError' if they fail to match, and otherwise returns--- one of them.-unifyExpTypes :: Exp -> Exp -> TermTypeM PatternType-unifyExpTypes e1 e2 = do-  e1_t <- expType e1-  e2_t <- expType e2-  unify (mkUsage (srclocOf e2) "requiring equality of types") (toStructural e1_t) (toStructural e2_t)-  return $ unifyTypeAliases e1_t e2_t---- | Assumes that the two types have already been unified.-unifyTypeAliases :: PatternType -> PatternType -> PatternType-unifyTypeAliases t1 t2 =-  case (t1, t2) of-    (Array als1 u1 et1 shape1, Array als2 u2 _ _) ->-      Array (als1<>als2) (min u1 u2) et1 shape1-    (Scalar (Record f1), Scalar (Record f2)) ->-      Scalar $ Record $ M.intersectionWith unifyTypeAliases f1 f2-    (Scalar (TypeVar als1 u v targs1), Scalar (TypeVar als2 _ _ targs2)) ->-      Scalar $ TypeVar (als1 <> als2) u v $ zipWith unifyTypeArg targs1 targs2-    _ -> t1-  where unifyTypeArg (TypeArgType t1' loc) (TypeArgType _ _) =-          TypeArgType t1' loc-        unifyTypeArg a _ = a----- General binding.--doNotShadow :: [String]-doNotShadow = ["&&", "||"]--data InferredType = NoneInferred-                  | Ascribed PatternType---checkPattern' :: UncheckedPattern -> InferredType-              -> TermTypeM Pattern--checkPattern' (PatternParens p loc) t =-  PatternParens <$> checkPattern' p t <*> pure loc--checkPattern' (Id name _ loc) _-  | name' `elem` doNotShadow =-      typeError loc $ "The " ++ name' ++ " operator may not be redefined."-  where name' = nameToString name--checkPattern' (Id name NoInfo loc) (Ascribed t) = do-  name' <- newID name-  return $ Id name' (Info t) loc-checkPattern' (Id name NoInfo loc) NoneInferred = do-  name' <- newID name-  t <- newTypeVar loc "t"-  return $ Id name' (Info t) loc--checkPattern' (Wildcard _ loc) (Ascribed t) =-  return $ Wildcard (Info $ t `setUniqueness` Nonunique) loc-checkPattern' (Wildcard NoInfo loc) NoneInferred = do-  t <- newTypeVar loc "t"-  return $ Wildcard (Info t) loc--checkPattern' (TuplePattern ps loc) (Ascribed t)-  | Just ts <- isTupleRecord t, length ts == length ps =-      TuplePattern <$> zipWithM checkPattern' ps (map Ascribed ts) <*> pure loc-checkPattern' p@(TuplePattern ps loc) (Ascribed t) = do-  ps_t <- replicateM (length ps) (newTypeVar loc "t")-  unify (mkUsage loc "matching a tuple pattern") (tupleRecord ps_t) $ toStructural t-  t' <- normaliseType t-  checkPattern' p $ Ascribed t'-checkPattern' (TuplePattern ps loc) NoneInferred =-  TuplePattern <$> mapM (`checkPattern'` NoneInferred) ps <*> pure loc--checkPattern' (RecordPattern p_fs _) _-  | Just (f, fp) <- find (("_" `isPrefixOf`) . nameToString . fst) p_fs =-      typeError fp $ unlines [ "Underscore-prefixed fields are not allowed."-                             , "Did you mean " ++-                               quote (drop 1 (nameToString f) ++ "=_") ++ "?"]--checkPattern' (RecordPattern p_fs loc) (Ascribed (Scalar (Record t_fs)))-  | sort (map fst p_fs) == sort (M.keys t_fs) =-    RecordPattern . M.toList <$> check <*> pure loc-    where check = traverse (uncurry checkPattern') $ M.intersectionWith (,)-                  (M.fromList p_fs) (fmap Ascribed t_fs)-checkPattern' p@(RecordPattern fields loc) (Ascribed t) = do-  fields' <- traverse (const $ newTypeVar loc "t") $ M.fromList fields--  when (sort (M.keys fields') /= sort (map fst fields)) $-    typeError loc $ "Duplicate fields in record pattern " ++ pretty p--  unify (mkUsage loc "matching a record pattern") (Scalar (Record fields')) $ toStructural t-  t' <- normaliseType t-  checkPattern' p $ Ascribed t'-checkPattern' (RecordPattern fs loc) NoneInferred =-  RecordPattern . M.toList <$> traverse (`checkPattern'` NoneInferred) (M.fromList fs) <*> pure loc--checkPattern' (PatternAscription p (TypeDecl t NoInfo) loc) maybe_outer_t = do-  (t', st, _) <- checkTypeExp t--  let st' = fromStruct st-  case maybe_outer_t of-    Ascribed outer_t -> do-      unify (mkUsage loc "explicit type ascription") (toStructural st) (toStructural outer_t)--      -- We also have to make sure that uniqueness and shapes match.-      -- This is done explicitly, because they are ignored by-      -- unification.-      st'' <- normaliseType st'-      outer_t' <- normaliseType outer_t-      case unifyTypesU unifyUniqueness st' outer_t' of-        Just outer_t'' ->-          PatternAscription <$> checkPattern' p (Ascribed outer_t'') <*>-          pure (TypeDecl t' (Info st)) <*> pure loc-        Nothing ->-          typeError loc $ "Cannot match type " ++ quote (pretty outer_t') ++ " with expected type " ++-          quote (pretty st'') ++ "."--    NoneInferred ->-      PatternAscription <$> checkPattern' p (Ascribed st') <*>-      pure (TypeDecl t' (Info st)) <*> pure loc- where unifyUniqueness u1 u2 = if u2 `subuniqueOf` u1 then Just u1 else Nothing--checkPattern' (PatternLit e NoInfo loc) (Ascribed t) = do-  e' <- checkExp e-  t' <- expType e'-  unify (mkUsage loc "matching against literal") (toStructural t') (toStructural t)-  return $ PatternLit e' (Info t') loc--checkPattern' (PatternLit e NoInfo loc) NoneInferred = do-  e' <- checkExp e-  t' <- expType e'-  return $ PatternLit e' (Info t') loc--checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed (Scalar (Sum cs)))-  | Just ts <- M.lookup n cs = do-      ps' <- zipWithM checkPattern' ps $ map Ascribed ts-      return $ PatternConstr n (Info (Scalar (Sum cs))) ps' loc--checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed t) = do-  t' <- newTypeVar loc "t"-  ps' <- mapM (`checkPattern'` NoneInferred) ps-  mustHaveConstr usage n t' (toStructural . patternType <$> ps')-  unify usage t' (toStructural t)-  t'' <- normaliseType t-  return $ PatternConstr n (Info t'') ps' loc-  where usage = mkUsage loc "matching against constructor"--checkPattern' (PatternConstr n NoInfo ps loc) NoneInferred = do-  ps' <- mapM (`checkPattern'` NoneInferred) ps-  t <- newTypeVar loc "t"-  mustHaveConstr usage n t (toStructural . patternType <$> ps')-  return $ PatternConstr n (Info t) ps' loc-  where usage = mkUsage loc "matching against constructor"--patternNameMap :: Pattern -> NameMap-patternNameMap = M.fromList . map asTerm . S.toList . patternIdents-  where asTerm v = ((Term, baseName $ identName v), qualName $ identName v)--checkPattern :: UncheckedPattern -> InferredType -> (Pattern -> TermTypeM a)-             -> TermTypeM a-checkPattern p t m = do-  checkForDuplicateNames [p]-  p' <- checkPattern' p t-  bindNameMap (patternNameMap p') $ m p'--binding :: [Ident] -> TermTypeM a -> TermTypeM a-binding bnds = check . localScope (`bindVars` bnds)-  where bindVars :: TermScope -> [Ident] -> TermScope-        bindVars = foldl bindVar--        bindVar :: TermScope -> Ident -> TermScope-        bindVar scope (Ident name (Info tp) _) =-          let inedges = boundAliases $ aliases tp-              update (BoundV l tparams in_t)-                -- If 'name' is record or sum-typed, don't alias the-                -- components to 'name', because these no identity-                -- beyond their components.-                | Array{} <- tp = BoundV l tparams (in_t `addAliases` S.insert (AliasBound name))-                | otherwise = BoundV l tparams in_t-              update b = b--              tp' = tp `addAliases` S.insert (AliasBound name)-          in scope { scopeVtable = M.insert name (BoundV Local [] tp') $-                                   adjustSeveral update inedges $-                                   scopeVtable scope-                   }--        adjustSeveral f = flip $ foldl $ flip $ M.adjust f--        -- Check whether the bound variables have been used correctly-        -- within their scope.-        check m = do-          (a, usages) <- collectBindingsOccurences m-          checkOccurences usages--          mapM_ (checkIfUsed usages) bnds--          return a--        -- Collect and remove all occurences in @bnds@.  This relies-        -- on the fact that no variables shadow any other.-        collectBindingsOccurences m = pass $ do-          (x, usage) <- listen m-          let (relevant, rest) = split usage-          return ((x, relevant), const rest)-          where split = unzip .-                        map (\occ ->-                             let (obs1, obs2) = divide $ observed occ-                                 occ_cons = divide <$> consumed occ-                                 con1 = fst <$> occ_cons-                                 con2 = snd <$> occ_cons-                             in (occ { observed = obs1, consumed = con1 },-                                 occ { observed = obs2, consumed = con2 }))-                names = S.fromList $ map identName bnds-                divide s = (s `S.intersection` names, s `S.difference` names)--bindingTypes :: [(VName, (TypeBinding, Constraint))] -> TermTypeM a -> TermTypeM a-bindingTypes types m = do-  lvl <- curLevel-  modifyConstraints (<>M.map ((lvl,) . snd) (M.fromList types))-  localScope extend m-  where extend scope = scope {-          scopeTypeTable = M.map fst (M.fromList types) <> scopeTypeTable scope-          }--bindingTypeParams :: [TypeParam] -> TermTypeM a -> TermTypeM a-bindingTypeParams tparams = binding (mapMaybe typeParamIdent tparams) .-                            bindingTypes (mapMaybe typeParamType tparams)-  where typeParamType (TypeParamType l v loc) =-          Just (v, (TypeAbbr l [] (Scalar (TypeVar () Nonunique (typeName v) [])),-                    ParamType l loc))-        typeParamType TypeParamDim{} =-          Nothing--typeParamIdent :: TypeParam -> Maybe Ident-typeParamIdent (TypeParamDim v loc) =-  Just $ Ident v (Info $ Scalar $ Prim $ Signed Int32) loc-typeParamIdent _ = Nothing--bindingIdent :: IdentBase NoInfo Name -> PatternType -> (Ident -> TermTypeM a)-             -> TermTypeM a-bindingIdent (Ident v NoInfo vloc) t m =-  bindSpaced [(Term, v)] $ do-    v' <- checkName Term v vloc-    let ident = Ident v' (Info t) vloc-    binding [ident] $ m ident--bindingPatternGroup :: [UncheckedTypeParam]-                    -> [UncheckedPattern]-                    -> ([TypeParam] -> [Pattern] -> TermTypeM a) -> TermTypeM a-bindingPatternGroup tps orig_ps m = do-  checkForDuplicateNames orig_ps-  checkTypeParams tps $ \tps' -> bindingTypeParams tps' $ do-    let descend ps' (p:ps) =-          checkPattern p NoneInferred $ \p' ->-            binding (S.toList $ patternIdents p') $ descend (p':ps') ps-        descend ps' [] = do-          -- Perform an observation of every type parameter.  This-          -- prevents unused-name warnings for otherwise unused-          -- dimensions.-          mapM_ observe $ mapMaybe typeParamIdent tps'-          let ps'' = reverse ps'-          checkShapeParamUses tps' $ map patternStructType ps''--          m tps' ps''--    descend [] orig_ps--bindingPattern :: PatternBase NoInfo Name -> InferredType-               -> (Pattern -> TermTypeM a) -> TermTypeM a-bindingPattern p t m = do-  checkForDuplicateNames [p]-  checkPattern p t $ \p' -> binding (S.toList $ patternIdents p') $ do-    -- Perform an observation of every declared dimension.  This-    -- prevents unused-name warnings for otherwise unused dimensions.-    mapM_ observe $ patternDims p'--    m p'--patternDims :: Pattern -> [Ident]-patternDims (PatternParens p _) = patternDims p-patternDims (TuplePattern pats _) = concatMap patternDims pats-patternDims (PatternAscription p (TypeDecl _ (Info t)) _) =-  patternDims p <> mapMaybe (dimIdent (srclocOf p)) (nestedDims t)-  where dimIdent _ AnyDim            = Nothing-        dimIdent _ (ConstDim _)      = Nothing-        dimIdent _ NamedDim{}        = Nothing-patternDims _ = []----- Main checkers---- | @require ts e@ causes a 'TypeError' if @expType e@ is not one of--- the types in @ts@.  Otherwise, simply returns @e@.-require :: String -> [PrimType] -> Exp -> TermTypeM Exp-require why ts e = do mustBeOneOf ts (mkUsage (srclocOf e) why) . toStructural =<< expType e-                      return e--unifies :: String -> TypeBase () () -> Exp -> TermTypeM Exp-unifies why t e = do-  unify (mkUsage (srclocOf e) why) t =<< toStructural <$> expType e-  return e---- The closure of a lambda or local function are those variables that--- it references, and which local to the current top-level function.-lexicalClosure :: [Pattern] -> Occurences -> TermTypeM Aliasing-lexicalClosure params closure = do-  vtable <- asks $ scopeVtable . termScope-  let isLocal v = case v `M.lookup` vtable of-                    Just (BoundV Local _ _) -> True-                    _ -> False-  return $ S.map AliasBound $ S.filter isLocal $-    allOccuring closure S.\\-    S.map identName (mconcat (map patternIdents params))--checkExp :: UncheckedExp -> TermTypeM Exp--checkExp (Literal val loc) =-  return $ Literal val loc--checkExp (StringLit vs loc) =-  return $ StringLit vs loc--checkExp (IntLit val NoInfo loc) = do-  t <- newTypeVar loc "t"-  mustBeOneOf anyNumberType (mkUsage loc "integer literal") t-  return $ IntLit val (Info $ vacuousShapeAnnotations $ fromStruct t) loc--checkExp (FloatLit val NoInfo loc) = do-  t <- newTypeVar loc "t"-  mustBeOneOf anyFloatType (mkUsage loc "float literal") t-  return $ FloatLit val (Info $ vacuousShapeAnnotations $ fromStruct t) loc--checkExp (TupLit es loc) =-  TupLit <$> mapM checkExp es <*> pure loc--checkExp (RecordLit fs loc) = do-  fs' <- evalStateT (mapM checkField fs) mempty--  return $ RecordLit fs' loc-  where checkField (RecordFieldExplicit f e rloc) = do-          errIfAlreadySet f rloc-          modify $ M.insert f rloc-          RecordFieldExplicit f <$> lift (checkExp e) <*> pure rloc-        checkField (RecordFieldImplicit name NoInfo rloc) = do-          errIfAlreadySet name rloc-          (QualName _ name', t) <- lift $ lookupVar rloc $ qualName name-          modify $ M.insert name rloc-          return $ RecordFieldImplicit name' (Info t) rloc--        errIfAlreadySet f rloc = do-          maybe_sloc <- gets $ M.lookup f-          case maybe_sloc of-            Just sloc ->-              lift $ typeError rloc $ "Field '" ++ pretty f ++-              " previously defined at " ++ locStr sloc ++ "."-            Nothing -> return ()--checkExp (ArrayLit all_es _ loc) =-  -- Construct the result type and unify all elements with it.  We-  -- only create a type variable for empty arrays; otherwise we use-  -- the type of the first element.  This significantly cuts down on-  -- the number of type variables generated for pathologically large-  -- multidimensional array literals.-  case all_es of-    [] -> do et <- newTypeVar loc "t"-             t <- arrayOfM loc et (ShapeDecl [AnyDim]) Unique-             return $ ArrayLit [] (Info t) loc-    e:es -> do-      e' <- checkExp e-      et <- expType e'-      es' <- mapM (unifies "type of first array element" (toStructural et) <=< checkExp) es-      et' <- normaliseType et-      t <- arrayOfM loc et' (ShapeDecl [AnyDim]) Unique-      return $ ArrayLit (e':es') (Info t) loc--checkExp (Range start maybe_step end NoInfo loc) = do-  start' <- require "use in range expression" anyIntType =<< checkExp start-  start_t <- toStructural <$> expType start'-  maybe_step' <- case maybe_step of-    Nothing -> return Nothing-    Just step -> do-      let warning = warn loc "First and second element of range are identical, this will produce an empty array."-      case (start, step) of-        (Literal x _, Literal y _) -> when (x == y) warning-        (Var x_name _ _, Var y_name _ _) -> when (x_name == y_name) warning-        _ -> return ()-      Just <$> (unifies "use in range expression" start_t =<< checkExp step)--  end' <- case end of-    DownToExclusive e -> DownToExclusive <$>-                         (unifies "use in range expression" start_t =<< checkExp e)-    UpToExclusive e -> UpToExclusive <$>-                       (unifies "use in range expression" start_t =<< checkExp e)-    ToInclusive e -> ToInclusive <$>-                     (unifies "use in range expression" start_t =<< checkExp e)--  t <- arrayOfM loc (vacuousShapeAnnotations start_t) (rank 1) Unique--  return $ Range start' maybe_step' end'-    (Info (t `setAliases` mempty)) loc--checkExp (Ascript e decl NoInfo loc) = do-  decl' <- checkTypeDecl decl-  e' <- checkExp e-  t <- expType e'-  let decl_t = unInfo $ expandedType decl'-  unify (mkUsage loc "explicit type ascription") (toStructural decl_t) (toStructural t)--  -- We also have to make sure that uniqueness matches.  This is done-  -- explicitly, because uniqueness is ignored by unification.-  t' <- normaliseType t-  decl_t' <- normaliseType decl_t-  unless (t' `subtypeOf` anyDimShapeAnnotations decl_t') $-    typeError loc $ "Type " ++ quote (pretty t') ++ " is not a subtype of " ++-    quote (pretty decl_t') ++ "."--  let t'' = flip unifyTypeAliases t' $ combineTypeShapes t' $ fromStruct decl_t'-  return $ Ascript e' decl' (Info t'') loc--checkExp (BinOp (op, oploc) NoInfo (e1,_) (e2,_) NoInfo loc) = do-  (op', ftype) <- lookupVar oploc op-  (e1', e1_arg) <- checkArg e1-  (e2', e2_arg) <- checkArg e2--  (p1_t, rt) <- checkApply loc ftype e1_arg-  (p2_t, rt') <- checkApply loc rt e2_arg--  return $ BinOp (op', oploc) (Info ftype)-    (e1', Info $ toStruct p1_t) (e2', Info $ toStruct p2_t)-    (Info rt') loc--checkExp (Project k e NoInfo loc) = do-  e' <- checkExp e-  t <- expType e'-  kt <- mustHaveField (mkUsage loc $ "projection of field " ++ quote (pretty k)) k t-  return $ Project k e' (Info kt) loc--checkExp (If e1 e2 e3 _ loc) =-  sequentially checkCond $ \e1' _ -> do-  ((e2', e3'), dflow) <- tapOccurences $ checkExp e2 `alternative` checkExp e3-  brancht <- unifyExpTypes e2' e3'-  let t' = addAliases brancht (`S.difference` S.map AliasBound (allConsumed dflow))-  zeroOrderType (mkUsage loc "returning value of this type from 'if' expression") "returned from branch" t'-  return $ If e1' e2' e3' (Info t') loc-  where checkCond = do-          e1' <- checkExp e1-          unify (mkUsage (srclocOf e1') "use as 'if' condition") (Scalar $ Prim Bool) . toStructural =<< expType e1'-          return e1'--checkExp (Parens e loc) =-  Parens <$> checkExp e <*> pure loc--checkExp (QualParens (modname, modnameloc) e loc) = do-  (modname',mod) <- lookupMod loc modname-  case mod of-    ModEnv env -> local (`withEnv` qualifyEnv modname' env) $ do-      e' <- checkExp e-      return $ QualParens (modname', modnameloc) e' loc-    ModFun{} ->-      typeError loc $ "Module " ++ pretty modname ++ " is a parametric module."-  where qualifyEnv modname' env =-          env { envNameMap = M.map (qualify' modname') $ envNameMap env }-        qualify' modname' (QualName qs name) =-          QualName (qualQuals modname' ++ [qualLeaf modname'] ++ qs) name--checkExp (Var qn NoInfo loc) = do-  -- The qualifiers of a variable is divided into two parts: first a-  -- possibly-empty sequence of module qualifiers, followed by a-  -- possible-empty sequence of record field accesses.  We use scope-  -- information to perform the split, by taking qualifiers off the-  -- end until we find a module.--  (qn', t, fields) <- findRootVar (qualQuals qn) (qualLeaf qn)--  foldM checkField (Var qn' (Info t) loc) fields--  where findRootVar qs name =-          (whenFound <$> lookupVar loc (QualName qs name)) `catchError` notFound qs name--        whenFound (qn', t) = (qn', t, [])--        notFound qs name err-          | null qs = throwError err-          | otherwise = do-              (qn', t, fields) <- findRootVar (init qs) (last qs) `catchError`-                                  const (throwError err)-              return (qn', t, fields++[name])--        checkField e k = do-          t <- expType e-          let usage = mkUsage loc $ "projection of field " ++ quote (pretty k)-          kt <- mustHaveField usage k t-          return $ Project k e (Info kt) loc--checkExp (Negate arg loc) = do-  arg' <- require "numeric negation" anyNumberType =<< checkExp arg-  return $ Negate arg' loc--checkExp (Apply e1 e2 NoInfo NoInfo loc) = do-  e1' <- checkExp e1-  (e2', arg) <- checkArg e2-  t <- expType e1'-  (t1, rt) <- checkApply loc t arg-  return $ Apply e1' e2' (Info $ diet t1) (Info rt) loc--checkExp (LetPat pat e body NoInfo loc) =-  sequentially (checkExp e) $ \e' e_occs -> do-    -- Not technically an ascription, but we want the pattern to have-    -- exactly the type of 'e'.-    t <- expType e'-    case anyConsumption e_occs of-      Just c ->-        let msg = "of value computed with consumption at " ++ locStr (location c)-        in zeroOrderType (mkUsage loc "consumption in right-hand side of 'let'-binding") msg t-      _ -> return ()--    incLevel $ bindingPattern pat (Ascribed $ anyDimShapeAnnotations t) $ \pat' -> do-      body' <- checkExp body-      body_t <- unscopeType (S.map identName $ patternIdents pat') <$> expType body'-      return $ LetPat pat' e' body' (Info body_t) loc--checkExp (LetFun name (tparams, params, maybe_retdecl, NoInfo, e) body loc) =-  sequentially (checkBinding (Just name, maybe_retdecl, tparams, params, e, loc)) $-  \(tparams', params', maybe_retdecl', rettype, e') closure -> do--    closure' <- lexicalClosure params' closure--    bindSpaced [(Term, name)] $ do-      name' <- checkName Term name loc--      let arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt-          ftype = foldr (arrow . patternParam) rettype params'-          entry = BoundV Local tparams' $ ftype `setAliases` closure'-          bindF scope = scope { scopeVtable = M.insert name' entry $ scopeVtable scope-                              , scopeNameMap = M.insert (Term, name) (qualName name') $-                                               scopeNameMap scope }-      body' <- localScope bindF $ checkExp body--      return $ LetFun name' (tparams', params', maybe_retdecl', Info rettype, e') body' loc--checkExp (LetWith dest src idxes ve body NoInfo loc) = do-  (t, _) <- newArrayType (srclocOf src) "src" $ length idxes-  let elemt = stripArray (length $ filter isFix idxes) t-  sequentially (checkIdent src) $ \src' _ -> do-    let src'' = Var (qualName $ identName src') (identType src') (srclocOf src)-    void $ unifies "type of target array" t src''--    unless (unique $ unInfo $ identType src') $-      typeError loc $ "Source " ++ quote (pretty (identName src)) ++-      " has type " ++ pretty (unInfo $ identType src') ++ ", which is not unique."-    vtable <- asks $ scopeVtable . termScope-    forM_ (aliases $ unInfo $ identType src') $ \v ->-      case aliasVar v `M.lookup` vtable of-        Just (BoundV Local _ v_t)-          | not $ unique v_t ->-              typeError loc $ "Source " ++ quote (pretty (identName src)) ++-              " aliases " ++ quote (prettyName (aliasVar v)) ++ ", which is not consumable."-        _ -> return ()--    idxes' <- mapM checkDimIndex idxes-    sequentially (unifies "type of target array" elemt =<< checkExp ve) $ \ve' _ -> do-      ve_t <- expType ve'-      when (AliasBound (identName src') `S.member` aliases ve_t) $-        badLetWithValue loc--      bindingIdent dest (unInfo (identType src') `setAliases` S.empty) $ \dest' -> do-        body' <- consuming src' $ checkExp body-        body_t <- unscopeType (S.singleton $ identName dest') <$> expType body'-        return $ LetWith dest' src' idxes' ve' body' (Info body_t) loc-  where isFix DimFix{} = True-        isFix _        = False--checkExp (Update src idxes ve loc) = do-  (t, _) <- newArrayType (srclocOf src) "src" $ length idxes-  let elemt = stripArray (length $ filter isFix idxes) t-  sequentially (checkExp ve >>= unifies "type of target array" elemt) $ \ve' _ ->-    sequentially (checkExp src >>= unifies "type of target array" t) $ \src' _ -> do--    idxes' <- mapM checkDimIndex idxes--    src_t <- expType src'-    unless (unique src_t) $-      typeError loc $ "Source " ++ quote (pretty src) ++-      " has type " ++ pretty src_t ++ ", which is not unique"--    let src_als = aliases src_t-    ve_t <- expType ve'-    unless (S.null $ src_als `S.intersection` aliases ve_t) $ badLetWithValue loc--    consume loc src_als-    return $ Update src' idxes' ve' loc-  where isFix DimFix{} = True-        isFix _        = False---- Record updates are a bit hacky, because we do not have row typing--- (yet?).  For now, we only permit record updates where we know the--- full type up to the field we are updating.-checkExp (RecordUpdate src fields ve NoInfo loc) = do-  src' <- checkExp src-  ve' <- checkExp ve-  a <- expType src'-  let usage = mkUsage loc "record update"-  r <- foldM (flip $ mustHaveField usage) a fields-  ve_t <- expType ve'-  unify usage (toStructural r) (toStructural ve_t)-  maybe_a' <- onRecordField (const ve_t) fields <$> expType src'-  case maybe_a' of-    Just a' -> return $ RecordUpdate src' fields ve' (Info a') loc-    Nothing -> typeError loc $ pretty $-               text "Full type of" </>-               indent 2 (ppr src) </>-               text " is not known at this point.  Add a size annotation to the original record to disambiguate."--checkExp (Index e idxes NoInfo loc) = do-  (t, _) <- newArrayType (srclocOf e) "e" $ length idxes-  e' <- unifies "being indexed at" t =<< checkExp e-  idxes' <- mapM checkDimIndex idxes-  t' <- anyDimShapeAnnotations .-        stripArray (length $ filter isFix idxes) <$>-        normaliseType (typeOf e')-  return $ Index e' idxes' (Info t') loc-  where isFix DimFix{} = True-        isFix _        = False--checkExp (Unsafe e loc) =-  Unsafe <$> checkExp e <*> pure loc--checkExp (Assert e1 e2 NoInfo loc) = do-  e1' <- require "being asserted" [Bool] =<< checkExp e1-  e2' <- checkExp e2-  return $ Assert e1' e2' (Info (pretty e1)) loc--checkExp (Lambda params body rettype_te NoInfo loc) =-  removeSeminullOccurences $ incLevel $-  bindingPatternGroup [] params $ \_ params' -> do-    rettype_checked <- traverse checkTypeExp rettype_te-    let declared_rettype =-          case rettype_checked of Just (_, st, _) -> Just st-                                  Nothing -> Nothing-    (body', closure) <--      tapOccurences $ noUnique $ checkFunBody body declared_rettype loc-    body_t <- expType body'-    let (rettype', rettype_st) =-          case rettype_checked of-            Just (te, st, _) -> (Just te, st)-            Nothing -> (Nothing, inferReturnUniqueness params' body_t)--    checkGlobalAliases params' body_t loc--    closure' <- lexicalClosure params' closure--    return $ Lambda params' body' rettype' (Info (closure', rettype_st)) loc--checkExp (OpSection op _ loc) = do-  (op', ftype) <- lookupVar loc op-  return $ OpSection op' (Info ftype) loc--checkExp (OpSectionLeft op _ e _ _ loc) = do-  (op', ftype) <- lookupVar loc op-  (e', e_arg) <- checkArg e-  (t1, rt) <- checkApply loc ftype e_arg-  case rt of-    Scalar (Arrow _ _ t2 rettype) ->-      return $ OpSectionLeft op' (Info ftype) e'-      (Info $ toStruct t1, Info $ toStruct t2) (Info rettype) loc-    _ -> typeError loc $-         "Operator section with invalid operator of type " ++ pretty ftype--checkExp (OpSectionRight op _ e _ _ loc) = do-  (op', ftype) <- lookupVar loc op-  (e', e_arg) <- checkArg e-  case ftype of-    Scalar (Arrow as1 m1 t1 (Scalar (Arrow as2 m2 t2 ret))) -> do-      (t2', Scalar (Arrow _ _ t1' rettype)) <--        checkApply loc (Scalar $ Arrow as2 m2 t2 $ Scalar $ Arrow as1 m1 t1 ret) e_arg-      return $ OpSectionRight op' (Info ftype) e'-        (Info $ toStruct t1', Info $ toStruct t2') (Info rettype) loc-    _ -> typeError loc $-         "Operator section with invalid operator of type " ++ pretty ftype--checkExp (ProjectSection fields NoInfo loc) = do-  a <- newTypeVar loc "a"-  let usage = mkUsage loc "projection at"-  b <- foldM (flip $ mustHaveField usage) a fields-  return $ ProjectSection fields (Info $ Scalar $ Arrow mempty Unnamed a b) loc--checkExp (IndexSection idxes NoInfo loc) = do-  (t, _) <- newArrayType loc "e" (length idxes)-  idxes' <- mapM checkDimIndex idxes-  let t' = stripArray (length $ filter isFix idxes) t-  return $ IndexSection idxes' (Info $ vacuousShapeAnnotations $ fromStruct $-                                Scalar $ Arrow mempty Unnamed t t') loc-  where isFix DimFix{} = True-        isFix _        = False--checkExp (DoLoop mergepat mergeexp form loopbody loc) =-  sequentially (checkExp mergeexp) $ \mergeexp' _ -> do--  zeroOrderType (mkUsage (srclocOf mergeexp) "use as loop variable")-    "used as loop variable" (typeOf mergeexp')--  merge_t <- do-    merge_t <- expType mergeexp'-    return $ Ascribed $ anyDimShapeAnnotations $ merge_t `setAliases` mempty--  -- First we do a basic check of the loop body to figure out which of-  -- the merge parameters are being consumed.  For this, we first need-  -- to check the merge pattern, which requires the (initial) merge-  -- expression.-  ---  -- Play a little with occurences to ensure it does not look like-  -- none of the merge variables are being used.-  ((mergepat', form', loopbody'), bodyflow) <--    case form of-      For i uboundexp -> do-        uboundexp' <- require "being the bound in a 'for' loop" anySignedType =<< checkExp uboundexp-        bound_t <- expType uboundexp'-        bindingIdent i bound_t $ \i' ->-          noUnique $ bindingPattern mergepat merge_t $-          \mergepat' -> onlySelfAliasing $ tapOccurences $ do-            loopbody' <- checkExp loopbody-            return (mergepat',-                    For i' uboundexp',-                    loopbody')--      ForIn xpat e -> do-        (arr_t, _) <- newArrayType (srclocOf e) "e" 1-        e' <- unifies "being iterated in a 'for-in' loop" arr_t =<< checkExp e-        t <- expType e'-        case t of-          _ | Just t' <- peelArray 1 t ->-                bindingPattern xpat (Ascribed t') $ \xpat' ->-                noUnique $ bindingPattern mergepat merge_t $-                \mergepat' -> onlySelfAliasing $ tapOccurences $ do-                  loopbody' <- checkExp loopbody-                  return (mergepat',-                          ForIn xpat' e',-                          loopbody')-            | otherwise ->-                typeError (srclocOf e) $-                "Iteratee of a for-in loop must be an array, but expression has type " ++ pretty t--      While cond ->-        noUnique $ bindingPattern mergepat merge_t $ \mergepat' ->-        onlySelfAliasing $ tapOccurences $-        sequentially (checkExp cond >>=-                      unifies "being the condition of a 'while' loop" (Scalar $ Prim Bool)) $ \cond' _ -> do-          loopbody' <- checkExp loopbody-          return (mergepat',-                  While cond',-                  loopbody')--  mergepat'' <- do-    loop_t <- expType loopbody'-    convergePattern mergepat' (allConsumed bodyflow) loop_t $-      mkUsage (srclocOf loopbody') "being (part of) the result of the loop body"--  let consumeMerge (Id _ (Info pt) ploc) mt-        | unique pt = consume ploc $ aliases mt-      consumeMerge (TuplePattern pats _) t | Just ts <- isTupleRecord t =-        zipWithM_ consumeMerge pats ts-      consumeMerge (PatternParens pat _) t =-        consumeMerge pat t-      consumeMerge (PatternAscription pat _ _) t =-        consumeMerge pat t-      consumeMerge _ _ =-        return ()-  consumeMerge mergepat'' =<< expType mergeexp'-  return $ DoLoop mergepat'' mergeexp' form' loopbody' loc--  where-    convergePattern pat body_cons body_t body_loc = do-      let consumed_merge = S.map identName (patternIdents pat) `S.intersection`-                           body_cons--          uniquePat (Wildcard (Info t) wloc) =-            Wildcard (Info $ t `setUniqueness` Nonunique) wloc-          uniquePat (PatternParens p ploc) =-            PatternParens (uniquePat p) ploc-          uniquePat (Id name (Info t) iloc)-            | name `S.member` consumed_merge =-                let t' = t `setUniqueness` Unique `setAliases` mempty-                in Id name (Info t') iloc-            | otherwise =-                let t' = case t of Scalar Record{} -> t-                                   _               -> t `setUniqueness` Nonunique-                in Id name (Info t') iloc-          uniquePat (TuplePattern pats ploc) =-            TuplePattern (map uniquePat pats) ploc-          uniquePat (RecordPattern fs ploc) =-            RecordPattern (map (fmap uniquePat) fs) ploc-          uniquePat (PatternAscription p t ploc) =-            PatternAscription p t ploc-          uniquePat p@PatternLit{} = p-          uniquePat (PatternConstr n t ps ploc) =-            PatternConstr n t (map uniquePat ps) ploc--          -- Make the pattern unique where needed.-          pat' = uniquePat pat--      -- Now check that the loop returned the right type.-      unify body_loc (toStructural body_t) $ toStructural $ patternType pat'-      body_t' <- normaliseType body_t-      pat_t <- normaliseType $ patternType pat'-      unless (body_t' `subtypeOf` pat_t) $-        unexpectedType (srclocOf body_loc)-        (toStructural body_t')-        [toStructural pat_t]--      -- Check that the new values of consumed merge parameters do not-      -- alias something bound outside the loop, AND that anything-      -- returned for a unique merge parameter does not alias anything-      -- else returned.  We also update the aliases for the pattern.-      bound_outside <- asks $ S.fromList . M.keys . scopeVtable . termScope-      let combAliases t1 t2 =-            case t1 of Scalar Record{} -> t1-                       _ -> t1 `addAliases` (<>aliases t2)--          checkMergeReturn (Id pat_v (Info pat_v_t) patloc) t-            | unique pat_v_t,-              v:_ <- S.toList $-                     S.map aliasVar (aliases t) `S.intersection` bound_outside =-                lift $ typeError loc $-                "Loop return value corresponding to merge parameter " ++-                quote (prettyName pat_v) ++ " aliases " ++ prettyName v ++ "."--            | otherwise = do-                (cons,obs) <- get-                unless (S.null $ aliases t `S.intersection` cons) $-                  lift $ typeError loc $-                  "Loop return value for merge parameter " ++-                  quote (prettyName pat_v) ++-                  " aliases other consumed merge parameter."-                when (unique pat_v_t &&-                      not (S.null (aliases t `S.intersection` (cons<>obs)))) $-                  lift $ typeError loc $-                  "Loop return value for consuming merge parameter " ++-                  quote (prettyName pat_v) ++ " aliases previously returned value."-                if unique pat_v_t-                  then put (cons<>aliases t, obs)-                  else put (cons, obs<>aliases t)--                return $ Id pat_v (Info (combAliases pat_v_t t)) patloc--          checkMergeReturn (Wildcard (Info pat_v_t) patloc) t =-            return $ Wildcard (Info (combAliases pat_v_t t)) patloc--          checkMergeReturn (PatternParens p _) t =-            checkMergeReturn p t--          checkMergeReturn (PatternAscription p _ _) t =-            checkMergeReturn p t--          checkMergeReturn (RecordPattern pfs patloc) (Scalar (Record tfs)) =-            RecordPattern . M.toList <$> sequence pfs' <*> pure patloc-            where pfs' = M.intersectionWith checkMergeReturn-                         (M.fromList pfs) tfs--          checkMergeReturn (TuplePattern pats patloc) t-            | Just ts <- isTupleRecord t =-                TuplePattern-                <$> zipWithM checkMergeReturn pats ts-                <*> pure patloc--          checkMergeReturn p _ =-            return p--      (pat'', (pat_cons, _)) <--        runStateT (checkMergeReturn pat' body_t') (mempty, mempty)--      let body_cons' = body_cons <> S.map aliasVar pat_cons-      if body_cons' == body_cons && patternType pat'' == patternType pat-        then return pat'-        else convergePattern pat'' body_cons' body_t' body_loc--checkExp (Constr name es NoInfo loc) = do-  t <- newTypeVar loc "t"-  es' <- mapM checkExp es-  ets <- mapM expType es'-  mustHaveConstr (mkUsage loc "use of constructor") name t (toStructural <$> ets)-  -- A sum value aliases *anything* that went into its construction.-  let als = mconcat (map aliases ets)-  return $ Constr name es' (Info $ t `addAliases` (<>als)) loc--checkExp (Match e cs NoInfo loc) =-  sequentially (checkExp e) $ \e' _ -> do-    mt <- expType e'-    (cs', t) <- checkCases mt cs-    zeroOrderType (mkUsage loc "being returned 'match'") "returned from pattern match" t-    return $ Match e' cs' (Info t) loc--checkCases :: PatternType-           -> NE.NonEmpty (CaseBase NoInfo Name)-           -> TermTypeM (NE.NonEmpty (CaseBase Info VName), PatternType)-checkCases mt rest_cs =-  case NE.uncons rest_cs of-    (c, Nothing) -> do-      (c', t) <- checkCase mt c-      return (c' NE.:| [], t)-    (c, Just cs) -> do-      (((c', c_t), (cs', cs_t)), dflow) <--        tapOccurences $ checkCase mt c `alternative` checkCases mt cs-      unify (mkUsage (srclocOf c) "pattern match") (toStructural c_t) (toStructural cs_t)-      let t = unifyTypeAliases c_t cs_t `addAliases`-              (`S.difference` S.map AliasBound (allConsumed dflow))-      return (NE.cons c' cs', t)--checkCase :: PatternType -> CaseBase NoInfo Name-          -> TermTypeM (CaseBase Info VName, PatternType)-checkCase mt (CasePat p caseExp loc) =-  bindingPattern p (Ascribed mt) $ \p' -> do-    caseExp' <- checkExp caseExp-    caseType <- expType caseExp'-    return (CasePat p' caseExp' loc, caseType)---- | An unmatched pattern. Used in in the generation of--- unmatched pattern warnings by the type checker.-data Unmatched p = UnmatchedNum p [ExpBase Info VName]-                 | UnmatchedBool p-                 | UnmatchedConstr p-                 | Unmatched p-                 deriving (Functor, Show)--instance Pretty (Unmatched (PatternBase Info VName)) where-  ppr um = case um of-      (UnmatchedNum p nums) -> ppr' p <+> text "where p is not one of" <+> ppr nums-      (UnmatchedBool p)     -> ppr' p-      (UnmatchedConstr p)     -> ppr' p-      (Unmatched p)         -> ppr' p-    where-      ppr' (PatternAscription p t _) = ppr p <> text ":" <+> ppr t-      ppr' (PatternParens p _)       = parens $ ppr' p-      ppr' (Id v _ _)                = pprName v-      ppr' (TuplePattern pats _)     = parens $ commasep $ map ppr' pats-      ppr' (RecordPattern fs _)      = braces $ commasep $ map ppField fs-        where ppField (name, t)      = text (nameToString name) <> equals <> ppr' t-      ppr' Wildcard{}                = text "_"-      ppr' (PatternLit e _ _)        = ppr e-      ppr' (PatternConstr n _ ps _)   = text "#" <> ppr n <+> sep (map ppr' ps)--unpackPat :: Pattern -> [Maybe Pattern]-unpackPat Wildcard{} = [Nothing]-unpackPat (PatternParens p _) = unpackPat p-unpackPat Id{} = [Nothing]-unpackPat (TuplePattern ps _) = Just <$> ps-unpackPat (RecordPattern fs _) = Just . snd <$> sortFields (M.fromList fs)-unpackPat (PatternAscription p _ _) = unpackPat p-unpackPat p@PatternLit{} = [Just p]-unpackPat p@PatternConstr{} = [Just p]--wildPattern :: Pattern -> Int -> Unmatched Pattern -> Unmatched Pattern-wildPattern (TuplePattern ps loc) pos um = wildTuple <$> um-  where wildTuple p = TuplePattern (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc-        ps' = map wildOut ps-        wildOut p = Wildcard (Info (patternType p)) (srclocOf p)-wildPattern (RecordPattern fs loc) pos um = wildRecord <$> um-  where wildRecord p =-          RecordPattern (take (pos - 1) fs' ++ [(fst (fs!!(pos - 1)), p)] ++ drop pos fs') loc-        fs' = map wildOut fs-        wildOut (f,p) = (f, Wildcard (Info (patternType p)) (srclocOf p))-wildPattern (PatternAscription p _ _) pos um = wildPattern p pos um-wildPattern (PatternParens p _) pos um = wildPattern p pos um-wildPattern (PatternConstr n t ps loc) pos um = wildConstr <$> um-  where wildConstr p = PatternConstr n t (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc-        ps' = map wildOut ps-        wildOut p = Wildcard (Info (patternType p)) (srclocOf p)-wildPattern _ _ um = um--checkUnmatched :: (MonadBreadCrumbs m, MonadTypeChecker m) => Exp -> m ()-checkUnmatched e = void $ checkUnmatched' e >> astMap tv e-  where checkUnmatched' (Match _ cs _ loc) =-          let ps = fmap (\(CasePat p _ _) -> p) cs-          in case unmatched id $ NE.toList ps of-              []  -> return ()-              ps' -> typeError loc $ "Unmatched cases in match expression: \n"-                                     ++ unlines (map (("  " ++) . pretty) ps')-        checkUnmatched' _ = return ()-        tv = ASTMapper { mapOnExp =-                           \e' -> checkUnmatched' e' >> return e'-                       , mapOnName        = pure-                       , mapOnQualName    = pure-                       , mapOnStructType  = pure-                       , mapOnPatternType = pure-                       }---- | A data type for constructor patterns.  This is used to make the--- code for detecting unmatched constructors cleaner, by separating--- the constructor-pattern cases from other cases.-data ConstrPat = ConstrPat { constrName :: Name-                           , constrType :: PatternType-                           , constrPayload :: [Pattern]-                           , constrSrcLoc :: SrcLoc-                           }---- Be aware of these fishy equality instances!--instance Eq ConstrPat where-  ConstrPat c1 _ _ _ == ConstrPat c2 _ _ _ = c1 == c2--instance Ord ConstrPat where-  ConstrPat c1 _ _ _ `compare` ConstrPat c2 _ _ _ = c1 `compare` c2--unmatched :: (Unmatched Pattern -> Unmatched Pattern) -> [Pattern] -> [Unmatched Pattern]-unmatched hole orig_ps-  | p:_ <- orig_ps,-    sameStructure labeledCols = do-    (i, cols) <- labeledCols-    let hole' = if isConstr p then hole else hole . wildPattern p i-    case sequence cols of-      Nothing -> []-      Just cs-        | all isPatternLit cs  -> map hole' $ localUnmatched cs-        | otherwise            -> unmatched hole' cs-  | otherwise = []--  where labeledCols = zip [1..] $ transpose $ map unpackPat orig_ps--        localUnmatched :: [Pattern] -> [Unmatched Pattern]-        localUnmatched [] = []-        localUnmatched ps'@(p':_) =-          case patternType p'  of-            Scalar (Sum cs'') ->-              -- We now know that we are matching a sum type, and thus-              -- that all patterns ps' are constructors (checked by-              -- 'all isPatternLit' before this function is called).-              let constrs   = M.keys cs''-                  matched   = mapMaybe constr ps'-                  unmatched' = map (UnmatchedConstr . buildConstr cs'') $-                               constrs \\ map constrName matched-             in case unmatched' of-                [] ->-                  let constrGroups   = group (sort matched)-                      removedConstrs = mapMaybe stripConstrs constrGroups-                      transposed     = (fmap . fmap) transpose removedConstrs-                      findUnmatched (pc, trans) = do-                        col <- trans-                        case col of-                          []           -> []-                          ((i, _):_) -> unmatched (wilder i pc) (map snd col)-                      wilder i pc s = (`PatternParens` noLoc) <$> wildPattern pc i s-                  in concatMap findUnmatched transposed-                _ -> unmatched'-            Scalar (Prim t) | not (any idOrWild ps') ->-              -- We now know that we are matching a sum type, and thus-              -- that all patterns ps' are literals (checked by 'all-              -- isPatternLit' before this function is called).-                case t of-                  Bool ->-                    let matched = nub $ mapMaybe (pExp >=> bool) $ filter isPatternLit ps'-                    in map (UnmatchedBool . buildBool (Scalar (Prim t))) $ [True, False] \\ matched-                  _ ->-                    let matched = mapMaybe pExp $ filter isPatternLit ps'-                    in [UnmatchedNum (buildId (Info $ Scalar $ Prim t) "p") matched]-            _ -> []--        isConstr PatternConstr{} = True-        isConstr (PatternParens p _) = isConstr p-        isConstr _ = False---        stripConstrs :: [ConstrPat] -> Maybe (Pattern, [[(Int, Pattern)]])-        stripConstrs (pc@ConstrPat{} : cs') = Just (unConstr pc, stripConstr pc : map stripConstr cs')-        stripConstrs [] = Nothing--        stripConstr :: ConstrPat -> [(Int, Pattern)]-        stripConstr (ConstrPat _ _  ps' _) = zip [1..] ps'--        sameStructure [] = True-        sameStructure (x:xs) = all (\y -> length y == length x' ) xs'-          where (x':xs') = map snd (x:xs)--        pExp (PatternLit e' _ _) = Just e'-        pExp _ = Nothing--        constr (PatternConstr c (Info t) ps loc) = Just $ ConstrPat c t ps loc-        constr (PatternParens p _) = constr p-        constr (PatternAscription p' _ _)  = constr p'-        constr _ = Nothing--        unConstr p =-          PatternConstr (constrName p) (Info $ constrType p) (constrPayload p) (constrSrcLoc p)--        isPatternLit PatternLit{} = True-        isPatternLit (PatternAscription p' _ _) = isPatternLit p'-        isPatternLit (PatternParens p' _)  = isPatternLit p'-        isPatternLit PatternConstr{} = True-        isPatternLit _ = False--        idOrWild Id{} = True-        idOrWild Wildcard{} = True-        idOrWild (PatternAscription p' _ _) = idOrWild p'-        idOrWild (PatternParens p' _) = idOrWild p'-        idOrWild _ = False--        bool (Literal (BoolValue b) _ ) = Just b-        bool _ = Nothing--        buildConstr m c =-          let t      = Scalar $ Sum m-              cs     = m M.! c-              wildCS = map (\ct -> Wildcard (Info ct) noLoc) cs-          in if null wildCS-               then PatternConstr c (Info t) [] noLoc-               else PatternParens (PatternConstr c (Info t) wildCS noLoc) noLoc-        buildBool t b =-          PatternLit (Literal (BoolValue b) noLoc) (Info (vacuousShapeAnnotations t)) noLoc-        buildId t n =-          -- The VName tag here will never be used since the value-          -- exists exclusively for printing warnings.-          Id (VName (nameFromString n) (-1)) t noLoc--checkIdent :: IdentBase NoInfo Name -> TermTypeM Ident-checkIdent (Ident name _ loc) = do-  (QualName _ name', vt) <- lookupVar loc (qualName name)-  return $ Ident name' (Info vt) loc--checkDimIndex :: DimIndexBase NoInfo Name -> TermTypeM DimIndex-checkDimIndex (DimFix i) =-  DimFix <$> (unifies "use as index" (Scalar $ Prim $ Signed Int32) =<< checkExp i)-checkDimIndex (DimSlice i j s) =-  DimSlice <$> check i <*> check j <*> check s-  where check = maybe (return Nothing) $-                fmap Just . unifies "use as index" (Scalar $ Prim $ Signed Int32) <=< checkExp--sequentially :: TermTypeM a -> (a -> Occurences -> TermTypeM b) -> TermTypeM b-sequentially m1 m2 = do-  (a, m1flow) <- collectOccurences m1-  (b, m2flow) <- collectOccurences $ m2 a m1flow-  occur $ m1flow `seqOccurences` m2flow-  return b--type Arg = (PatternType, Occurences, SrcLoc)--argType :: Arg -> PatternType-argType (t, _, _) = t--checkArg :: UncheckedExp -> TermTypeM (Exp, Arg)-checkArg arg = do-  (arg', dflow) <- collectOccurences $ checkExp arg-  arg_t <- expType arg'-  return (arg', (arg_t, dflow, srclocOf arg'))--checkApply :: SrcLoc -> PatternType -> Arg-           -> TermTypeM (PatternType, PatternType)-checkApply loc (Scalar (Arrow as _ tp1 tp2)) (argtype, dflow, argloc) = do-  unify (mkUsage argloc "use as function argument") (toStructural tp1) (toStructural argtype)--  -- Perform substitutions of instantiated variables in the types.-  tp1' <- normaliseType tp1-  tp2' <- normaliseType tp2-  argtype' <- normaliseType argtype--  occur [observation as loc]--  checkOccurences dflow-  occurs <- consumeArg argloc argtype' (diet tp1')--  case anyConsumption dflow of-    Just c ->-      let msg = "of value computed with consumption at " ++ locStr (location c)-      in zeroOrderType (mkUsage argloc "potential consumption in expression") msg tp1-    _ -> return ()--  occur $ dflow `seqOccurences` occurs-  let tp2'' = anyDimShapeAnnotations $ returnType tp2' (diet tp1') argtype'-  return (tp1', tp2'')--checkApply loc tfun@(Scalar TypeVar{}) arg = do-  tv <- newTypeVar loc "b"-  unify (mkUsage loc "use as function") (toStructural tfun) $-    Scalar $ Arrow mempty Unnamed (toStructural (argType arg)) tv-  constraints <- getConstraints-  checkApply loc (applySubst (`lookupSubst` constraints) tfun) arg--checkApply loc ftype arg =-  typeError loc $-  "Attempt to apply an expression of type " ++ pretty ftype ++-  " to an argument of type " ++ pretty (argType arg) ++ "."---- | @returnType ret_type arg_diet arg_type@ gives result of applying--- an argument the given types to a function with the given return--- type, consuming the argument with the given diet.-returnType :: PatternType-           -> Diet-           -> PatternType-           -> PatternType-returnType (Array _ Unique et shape) _ _ =-  Array mempty Unique et shape-returnType (Array als Nonunique et shape) d arg =-  Array (als<>arg_als) Unique et shape -- Intentional!-  where arg_als = aliases $ maskAliases arg d-returnType (Scalar (Record fs)) d arg =-  Scalar $ Record $ fmap (\et -> returnType et d arg) fs-returnType (Scalar (Prim t)) _ _ =-  Scalar $ Prim t-returnType (Scalar (TypeVar _ Unique t targs)) _ _ =-  Scalar $ TypeVar mempty Unique t targs-returnType (Scalar (TypeVar als Nonunique t targs)) d arg =-  Scalar $ TypeVar (als<>arg_als) Unique t targs -- Intentional!-  where arg_als = aliases $ maskAliases arg d-returnType (Scalar (Arrow _ v t1 t2)) d arg =-  Scalar $ Arrow als v (t1 `setAliases` mempty) (t2 `setAliases` als)-  where als = aliases $ maskAliases arg d-returnType (Scalar (Sum cs)) d arg =-  Scalar $ Sum $ (fmap . fmap) (\et -> returnType et d arg) cs---- | @t `maskAliases` d@ removes aliases (sets them to 'mempty') from--- the parts of @t@ that are denoted as consumed by the 'Diet' @d@.-maskAliases :: Monoid as =>-               TypeBase shape as-            -> Diet-            -> TypeBase shape as-maskAliases t Consume = t `setAliases` mempty-maskAliases t Observe = t-maskAliases (Scalar (Record ets)) (RecordDiet ds) =-  Scalar $ Record $ M.intersectionWith maskAliases ets ds-maskAliases t FuncDiet{} = t-maskAliases _ _ = error "Invalid arguments passed to maskAliases."--consumeArg :: SrcLoc -> PatternType -> Diet -> TermTypeM [Occurence]-consumeArg loc (Scalar (Record ets)) (RecordDiet ds) =-  concat . M.elems <$> traverse (uncurry $ consumeArg loc) (M.intersectionWith (,) ets ds)-consumeArg loc (Array _ Nonunique _ _) Consume =-  typeError loc "Consuming parameter passed non-unique argument."-consumeArg loc (Scalar (Arrow _ _ t1 _)) (FuncDiet d _)-  | not $ contravariantArg t1 d =-      typeError loc "Non-consuming higher-order parameter passed consuming argument."-  where contravariantArg (Array _ Unique _ _) Observe =-          False-        contravariantArg (Scalar (TypeVar _ Unique _ _)) Observe =-          False-        contravariantArg (Scalar (Record ets)) (RecordDiet ds) =-          and (M.intersectionWith contravariantArg ets ds)-        contravariantArg (Scalar (Arrow _ _ tp tr)) (FuncDiet dp dr) =-          contravariantArg tp dp && contravariantArg tr dr-        contravariantArg _ _ =-          True-consumeArg loc (Scalar (Arrow _ _ _ t2)) (FuncDiet _ pd) =-  consumeArg loc t2 pd-consumeArg loc at Consume = return [consumption (aliases at) loc]-consumeArg loc at _       = return [observation (aliases at) loc]--checkOneExp :: UncheckedExp -> TypeM ([TypeParam], Exp)-checkOneExp e = fmap fst . runTermTypeM $ do-  e' <- checkExp e-  let t = toStruct $ typeOf e'-  tparams <- letGeneralise [] t-  fixOverloadedTypes-  e'' <- updateExpTypes e'-  return (tparams, e'')---- | Type-check a top-level (or module-level) function definition.--- Despite the name, this is also used for checking constant--- definitions, by treating them as 0-ary functions.-checkFunDef :: (Name, Maybe UncheckedTypeExp,-                [UncheckedTypeParam], [UncheckedPattern],-                UncheckedExp, SrcLoc)-            -> TypeM (VName, [TypeParam], [Pattern], Maybe (TypeExp VName), StructType, Exp)-checkFunDef (fname, maybe_retdecl, tparams, params, body, loc) =-  fmap fst $ runTermTypeM $ do-  (tparams', params', maybe_retdecl', rettype', body') <--    checkBinding (Just fname, maybe_retdecl, tparams, params, body, loc)--  -- Since this is a top-level function, we also resolve overloaded-  -- types, using either defaults or complaining about ambiguities.-  fixOverloadedTypes--  -- Then replace all inferred types in the body and parameters.-  body'' <- updateExpTypes body'-  params'' <- updateExpTypes params'-  maybe_retdecl'' <- traverse updateExpTypes maybe_retdecl'-  rettype'' <- normaliseType rettype'--  -- Check if pattern matches are exhaustive and yield-  -- errors if not.-  checkUnmatched body''--  bindSpaced [(Term, fname)] $ do-    fname' <- checkName Term fname loc-    when (nameToString fname `elem` doNotShadow) $-      typeError loc $ "The " ++ nameToString fname ++ " operator may not be redefined."--    return (fname', tparams', params'', maybe_retdecl'', rettype'', body'')---- | This is "fixing" as in "setting them", not "correcting them".  We--- only make very conservative fixing.-fixOverloadedTypes :: TermTypeM ()-fixOverloadedTypes = getConstraints >>= mapM_ fixOverloaded . M.toList . M.map snd-  where fixOverloaded (v, Overloaded ots usage)-          | Signed Int32 `elem` ots = do-              unify usage (Scalar (TypeVar () Nonunique (typeName v) [])) $-                Scalar $ Prim $ Signed Int32-              warn usage "Defaulting ambiguous type to `i32`."-          | FloatType Float64 `elem` ots = do-              unify usage (Scalar (TypeVar () Nonunique (typeName v) [])) $-                Scalar $ Prim $ FloatType Float64-              warn usage "Defaulting ambiguous type to `f64`."-          | otherwise =-              typeError usage $-              unlines ["Type is ambiguous (could be one of " ++ intercalate ", " (map pretty ots) ++ ").",-                       "Add a type annotation to disambiguate the type."]--        fixOverloaded (_, NoConstraint _ usage) =-          typeError usage $ unlines ["Type of expression is ambiguous.",-                                     "Add a type annotation to disambiguate the type."]--        fixOverloaded (_, Equality usage) =-          typeError usage $ unlines ["Type is ambiguous (must be equality type).",-                                     "Add a type annotation to disambiguate the type."]--        fixOverloaded (_, HasFields fs usage) =-          typeError usage $ unlines ["Type is ambiguous.  Must be record with fields:",-                                     unlines $ map field $ M.toList fs,-                                     "Add a type annotation to disambiguate the type."]-          where field (l, t) = pretty $ indent 2 $ ppr l <> colon <+> align (ppr t)--        fixOverloaded (_, HasConstrs cs usage) =-          typeError usage $ unlines [ "Type is ambiguous (must be a sum type with constructors: " ++-                                      pretty (Sum cs) ++ ")."-                                    , "Add a type annotation to disambiguate the type."]--        fixOverloaded _ = return ()--checkBinding :: (Maybe Name, Maybe UncheckedTypeExp,-                 [UncheckedTypeParam], [UncheckedPattern],-                 UncheckedExp, SrcLoc)-             -> TermTypeM ([TypeParam], [Pattern], Maybe (TypeExp VName), StructType, Exp)-checkBinding (fname, maybe_retdecl, tparams, params, body, loc) =-  noUnique $ incLevel $ bindingPatternGroup tparams params $ \tparams' params' -> do-    maybe_retdecl' <- traverse checkTypeExp maybe_retdecl--    body' <- checkFunBody body ((\(_,t,_)->t) <$> maybe_retdecl') (maybe loc srclocOf maybe_retdecl)--    params'' <- updateExpTypes params'-    body_t <- expType body'--    (maybe_retdecl'', rettype) <- case maybe_retdecl' of-      Just (retdecl', retdecl_type, _) -> do-        let rettype_structural = toStructural retdecl_type-        checkReturnAlias rettype_structural params'' body_t--        when (null params) $ nothingMustBeUnique loc rettype_structural--        warnOnDubiousShapeAnnotations loc params'' retdecl_type--        return (Just retdecl', retdecl_type)-      Nothing-        | null params ->-            return (Nothing, toStruct $ body_t `setUniqueness` Nonunique)-        | otherwise ->-            return (Nothing, inferReturnUniqueness params'' body_t)--    let fun_t = foldFunType (map patternStructType params'') rettype-    tparams'' <- letGeneralise tparams' fun_t--    checkGlobalAliases params'' body_t loc--    return (tparams'', params'', maybe_retdecl'', rettype, body')--  where -- | Check that unique return values do not alias a-        -- non-consumed parameter.-        checkReturnAlias rettp params' =-          foldM_ (checkReturnAlias' params') S.empty . returnAliasing rettp-        checkReturnAlias' params' seen (Unique, names)-          | any (`S.member` S.map snd seen) $ S.toList names =-              uniqueReturnAliased fname loc-          | otherwise = do-              notAliasingParam params' names-              return $ seen `S.union` tag Unique names-        checkReturnAlias' _ seen (Nonunique, names)-          | any (`S.member` seen) $ S.toList $ tag Unique names =-            uniqueReturnAliased fname loc-          | otherwise = return $ seen `S.union` tag Nonunique names--        notAliasingParam params' names =-          forM_ params' $ \p ->-          let consumedNonunique p' =-                not (unique $ unInfo $ identType p') && (identName p' `S.member` names)-          in case find consumedNonunique $ S.toList $ patternIdents p of-               Just p' ->-                 returnAliased fname (baseName $ identName p') loc-               Nothing ->-                 return ()--        tag u = S.map (u,)--        returnAliasing (Scalar (Record ets1)) (Scalar (Record ets2)) =-          concat $ M.elems $ M.intersectionWith returnAliasing ets1 ets2-        returnAliasing expected got =-          [(uniqueness expected, S.map aliasVar $ aliases got)]--warnOnDubiousShapeAnnotations :: SrcLoc -> [Pattern] -> StructType -> TermTypeM ()-warnOnDubiousShapeAnnotations loc params rettype =-  onDubiousNames $ S.filter patternNameButNotParamName $-  mconcat $ map typeDimNames $-  rettype : map patternStructType params-  where named (Named v) = Just v-        named Unnamed = Nothing-        param_names = S.fromList $ mapMaybe (named . fst . patternParam) params-        all_pattern_names = S.map identName $ mconcat $ map patternIdents params-        patternNameButNotParamName v = v `S.member` all_pattern_names && not (v `S.member` param_names)-        onDubiousNames dubious-          | S.null dubious = return ()-          | otherwise = warn loc $ unlines-                        [ "Size annotations in parameter and/or return type refers to the following names,"-                        , "which will not be visible to the caller, because they are nested in tuples or records:"-                        , "  " ++ intercalate ", " (map (quote . prettyName) $ S.toList dubious)-                        , "To eliminate this warning, make these names parameters on their own."]--checkGlobalAliases :: [Pattern] -> PatternType -> SrcLoc -> TermTypeM ()-checkGlobalAliases params body_t loc = do-  vtable <- asks $ scopeVtable . termScope-  let isLocal v = case v `M.lookup` vtable of-                    Just (BoundV Local _ _) -> True-                    _ -> False-  let als = filter (not . isLocal) $ S.toList $-            boundArrayAliases body_t `S.difference`-            S.map identName (mconcat (map patternIdents params))-  case als of-    v:_ | not $ null params ->-      typeError loc $-      unlines [ "Function result aliases the free variable " <>-                quote (prettyName v) <> "."-              , "Use " ++ quote "copy" ++ " to break the aliasing."]-    _ ->-      return ()---inferReturnUniqueness :: [Pattern] -> PatternType -> StructType-inferReturnUniqueness params t =-  let forbidden = aliasesMultipleTimes t-      uniques = uniqueParamNames params-      delve (Scalar (Record fs)) =-        Scalar $ Record $ M.map delve fs-      delve t'-        | all (`S.member` uniques) (boundArrayAliases t'),-          not $ any ((`S.member` forbidden) . aliasVar) (aliases t') =-            toStruct t'-        | otherwise =-            toStruct $ t' `setUniqueness` Nonunique-  in delve t---- An alias inhibits uniqueness if it is used in disjoint values.-aliasesMultipleTimes :: PatternType -> Names-aliasesMultipleTimes = S.fromList . map fst . filter ((>1) . snd) . M.toList . delve-  where delve (Scalar (Record fs)) =-          foldl' (M.unionWith (+)) mempty $ map delve $ M.elems fs-        delve t =-          M.fromList $ zip (map aliasVar $ S.toList (aliases t)) $ repeat (1::Int)--uniqueParamNames :: [Pattern] -> Names-uniqueParamNames =-  S.fromList . map identName-  . filter (unique . unInfo . identType)-  . S.toList . mconcat . map patternIdents--boundArrayAliases :: PatternType -> S.Set VName-boundArrayAliases (Array als _ _ _) = boundAliases als-boundArrayAliases (Scalar Prim{}) = mempty-boundArrayAliases (Scalar (Record fs)) = foldMap boundArrayAliases fs-boundArrayAliases (Scalar (TypeVar als _ _ _)) = boundAliases als-boundArrayAliases (Scalar Arrow{}) = mempty-boundArrayAliases (Scalar (Sum fs)) =-  mconcat $ concatMap (map boundArrayAliases) $ M.elems fs---- | The set of in-scope variables that are being aliased.-boundAliases :: Aliasing -> S.Set VName-boundAliases = S.map aliasVar . S.filter bound-  where bound AliasBound{} = True-        bound AliasFree{} = False--nothingMustBeUnique :: SrcLoc -> TypeBase () () -> TermTypeM ()-nothingMustBeUnique loc = check-  where check (Array _ Unique _ _) = bad-        check (Scalar (TypeVar _ Unique _ _)) = bad-        check (Scalar (Record fs)) = mapM_ check fs-        check _ = return ()-        bad = typeError loc "A top-level constant cannot have a unique type."--letGeneralise :: [TypeParam] -> StructType -> TermTypeM [TypeParam]-letGeneralise tparams t = do-  now_substs <- getConstraints-  -- Candidates for let-generalisation are those type variables that-  ---  -- (1) were not known before we checked this function, and-  ---  -- (2) are not used in the (new) definition of any type variables-  -- known before we checked this function.-  ---  -- (3) are not referenced from an overloaded type (for example,-  -- are the element types of an incompletely resolved record type).-  -- This is a bit more restrictive than I'd like, and SML for-  -- example does not have this restriction.-  ---  -- Criteria (1) and (2) is implemented by looking at the binding-  -- level of the type variables.-  let keep_type_vars = overloadedTypeVars now_substs--  cur_lvl <- curLevel-  let candiate k (lvl, _) = (k `S.notMember` keep_type_vars) && lvl >= cur_lvl-      new_substs = M.filterWithKey candiate now_substs-  tparams' <- closeOverTypes new_substs tparams t--  -- We keep those type variables that were not closed over by-  -- let-generalisation.-  modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` map typeParamName tparams'--  return tparams'--checkFunBody :: ExpBase NoInfo Name-             -> Maybe StructType-             -> SrcLoc-             -> TermTypeM Exp-checkFunBody body maybe_rettype _loc = do-  body' <- checkExp body--  -- Unify body return type with return annotation, if one exists.-  case maybe_rettype of-    Just rettype -> do-      let rettype_structural = toStructural rettype-      void $ unifies "return type annotation" rettype_structural body'-      -- We also have to make sure that uniqueness matches.  This is done-      -- explicitly, because uniqueness is ignored by unification.-      rettype' <- normaliseType rettype-      body_t <- expType body'-      unless (body_t `subtypeOf` anyDimShapeAnnotations rettype') $-        typeError (srclocOf body) $ "Body type " ++ quote (pretty body_t) ++-        " is not a subtype of annotated type " ++-        quote (pretty rettype') ++ "."--    Nothing -> return ()--  return body'---- | Find at all type variables in the given type that are covered by--- the constraints, and produce type parameters that close over them.------ The passed-in list of type parameters is always prepended to the--- produced list of type parameters.-closeOverTypes :: Constraints -> [TypeParam] -> StructType -> TermTypeM [TypeParam]-closeOverTypes substs tparams t =-  fmap ((tparams++) . catMaybes) $ mapM closeOver $ M.toList $ M.map snd to_close_over-  where to_close_over = M.filterWithKey (\k _ -> k `S.member` visible) substs-        visible = typeVars t--        -- Avoid duplicate type parameters.-        closeOver (k, _)-          | k `elem` map typeParamName tparams =-              return Nothing-        closeOver (k, NoConstraint Unlifted usage) =-          return $ Just $ TypeParamType Unlifted k $ srclocOf usage-        closeOver (k, NoConstraint _ usage) =-          return $ Just $ TypeParamType Lifted k $ srclocOf usage-        closeOver (k, ParamType l loc) =-          return $ Just $ TypeParamType l k loc-        closeOver (_, _) =-          return Nothing----- Consumption--occur :: Occurences -> TermTypeM ()-occur = tell---- | Proclaim that we have made read-only use of the given variable.-observe :: Ident -> TermTypeM ()-observe (Ident nm (Info t) loc) =-  let als = AliasBound nm `S.insert` aliases t-  in occur [observation als loc]---- | Proclaim that we have written to the given variable.-consume :: SrcLoc -> Aliasing -> TermTypeM ()-consume loc als = do-  vtable <- asks $ scopeVtable . termScope-  let consumable v = case M.lookup v vtable of-                       Just (BoundV Local _ t)-                         | arrayRank t > 0 -> unique t-                         | otherwise -> True-                       _ -> False-  case filter (not . consumable) $ map aliasVar $ S.toList als of-    v:_ -> typeError loc $ "Attempt to consume variable " ++ quote (prettyName v)-           ++ ", which is not allowed."-    [] -> occur [consumption als loc]---- | Proclaim that we have written to the given variable, and mark--- accesses to it and all of its aliases as invalid inside the given--- computation.-consuming :: Ident -> TermTypeM a -> TermTypeM a-consuming (Ident name (Info t) loc) m = do-  consume loc $ AliasBound name `S.insert` aliases t-  localScope consume' m-  where consume' scope =-          scope { scopeVtable = M.insert name (WasConsumed loc) $ scopeVtable scope }--collectOccurences :: TermTypeM a -> TermTypeM (a, Occurences)-collectOccurences m = pass $ do-  (x, dataflow) <- listen m-  return ((x, dataflow), const mempty)--tapOccurences :: TermTypeM a -> TermTypeM (a, Occurences)-tapOccurences = listen--removeSeminullOccurences :: TermTypeM a -> TermTypeM a-removeSeminullOccurences = censor $ filter $ not . seminullOccurence--checkIfUsed :: Occurences -> Ident -> TermTypeM ()-checkIfUsed occs v-  | not $ identName v `S.member` allOccuring occs,-    not $ "_" `isPrefixOf` prettyName (identName v) =-      warn (srclocOf v) $ "Unused variable " ++ quote (pretty $ baseName $ identName v) ++ "."-  | otherwise =-      return ()--alternative :: TermTypeM a -> TermTypeM b -> TermTypeM (a,b)-alternative m1 m2 = pass $ do-  (x, occurs1) <- listen m1-  (y, occurs2) <- listen m2-  checkOccurences occurs1-  checkOccurences occurs2-  let usage = occurs1 `altOccurences` occurs2-  return ((x, y), const usage)---- | Make all bindings nonunique.-noUnique :: TermTypeM a -> TermTypeM a-noUnique = localScope (\scope -> scope { scopeVtable = M.map set $ scopeVtable scope})-  where set (BoundV l tparams t)    = BoundV l tparams $ t `setUniqueness` Nonunique-        set (OverloadedF ts pts rt) = OverloadedF ts pts rt-        set EqualityF               = EqualityF-        set (WasConsumed loc)       = WasConsumed loc--onlySelfAliasing :: TermTypeM a -> TermTypeM a-onlySelfAliasing = localScope (\scope -> scope { scopeVtable = M.mapWithKey set $ scopeVtable scope})-  where set k (BoundV l tparams t)    = BoundV l tparams $-                                        t `addAliases` S.intersection (S.singleton (AliasBound k))-        set _ (OverloadedF ts pts rt) = OverloadedF ts pts rt-        set _ EqualityF               = EqualityF-        set _ (WasConsumed loc)       = WasConsumed loc--arrayOfM :: (Pretty (ShapeDecl dim), Monoid as) =>-            SrcLoc-         -> TypeBase dim as -> ShapeDecl dim -> Uniqueness-         -> TermTypeM (TypeBase dim as)-arrayOfM loc t shape u = do-  zeroOrderType (mkUsage loc "use as array element") "used in array" t-  return $ arrayOf t shape u---- | Perform substitutions of instantiated variables on the type--- annotations (including the instance lists) of an expression, or--- something else.-updateExpTypes :: ASTMappable e => e -> TermTypeM e-updateExpTypes e = do-  constraints <- getConstraints-  let look = (`lookupSubst` constraints)-      tv = ASTMapper { mapOnExp         = astMap tv-                     , mapOnName        = pure-                     , mapOnQualName    = pure-                     , mapOnStructType  = pure . applySubst look-                     , mapOnPatternType = pure . applySubst look-                     }-  astMap tv e+{-# Language OverloadedStrings #-}+-- | Facilities for type-checking Futhark terms.  Checking a term+-- requires a little more context to track uniqueness and such.+--+-- Type inference is implemented through a variation of+-- Hindley-Milner.  The main complication is supporting the rich+-- number of built-in language constructs, as well as uniqueness+-- types.  This is mostly done in an ad hoc way, and many programs+-- will require the programmer to fall back on type annotations.+module Language.Futhark.TypeChecker.Terms+  ( checkOneExp+  , checkFunDef+  )+where++import Control.Monad.Identity+import Control.Monad.Except+import Control.Monad.State+import Control.Monad.RWS hiding (Sum)+import Control.Monad.Writer hiding (Sum)+import Data.Bifunctor+import Data.Char (isAscii)+import Data.Either+import Data.List+import qualified Data.List.NonEmpty as NE+import Data.Loc+import Data.Maybe+import qualified Data.Map.Strict as M+import qualified Data.Set as S++import Prelude hiding (mod)++import Language.Futhark hiding (unscopeType)+import Language.Futhark.Semantic (includeToString)+import Language.Futhark.Traversals+import Language.Futhark.TypeChecker.Monad hiding (BoundV, checkQualNameWithEnv)+import Language.Futhark.TypeChecker.Types hiding (checkTypeDecl)+import Language.Futhark.TypeChecker.Unify hiding (Usage)+import qualified Language.Futhark.TypeChecker.Types as Types+import qualified Language.Futhark.TypeChecker.Monad as TypeM+import Futhark.Util.Pretty hiding (space, bool, group)++--- Uniqueness++data Usage = Consumed SrcLoc+           | Observed SrcLoc+           deriving (Eq, Ord, Show)++type Names = S.Set VName++-- | The consumption set is a Maybe so we can distinguish whether a+-- consumption took place, but the variable went out of scope since,+-- or no consumption at all took place.+data Occurence = Occurence { observed :: Names+                           , consumed :: Maybe Names+                           , location :: SrcLoc+                           }+             deriving (Eq, Show)++instance Located Occurence where+  locOf = locOf . location++observation :: Aliasing -> SrcLoc -> Occurence+observation = flip Occurence Nothing . S.map aliasVar++consumption :: Aliasing -> SrcLoc -> Occurence+consumption = Occurence S.empty . Just . S.map aliasVar++-- | A null occurence is one that we can remove without affecting+-- anything.+nullOccurence :: Occurence -> Bool+nullOccurence occ = S.null (observed occ) && isNothing (consumed occ)++-- | A seminull occurence is one that does not contain references to+-- any variables in scope.  The big difference is that a seminull+-- occurence may denote a consumption, as long as the array that was+-- consumed is now out of scope.+seminullOccurence :: Occurence -> Bool+seminullOccurence occ = S.null (observed occ) && maybe True S.null (consumed occ)++type Occurences = [Occurence]++type UsageMap = M.Map VName [Usage]++usageMap :: Occurences -> UsageMap+usageMap = foldl comb M.empty+  where comb m (Occurence obs cons loc) =+          let m' = S.foldl' (ins $ Observed loc) m obs+          in S.foldl' (ins $ Consumed loc) m' $ fromMaybe mempty cons+        ins v m k = M.insertWith (++) k [v] m++combineOccurences :: VName -> Usage -> Usage -> TermTypeM Usage+combineOccurences _ (Observed loc) (Observed _) = return $ Observed loc+combineOccurences name (Consumed wloc) (Observed rloc) =+  useAfterConsume (baseName name) rloc wloc+combineOccurences name (Observed rloc) (Consumed wloc) =+  useAfterConsume (baseName name) rloc wloc+combineOccurences name (Consumed loc1) (Consumed loc2) =+  consumeAfterConsume (baseName name) (max loc1 loc2) (min loc1 loc2)++checkOccurences :: Occurences -> TermTypeM ()+checkOccurences = void . M.traverseWithKey comb . usageMap+  where comb _    []     = return ()+        comb name (u:us) = foldM_ (combineOccurences name) u us++allObserved :: Occurences -> Names+allObserved = S.unions . map observed++allConsumed :: Occurences -> Names+allConsumed = S.unions . map (fromMaybe mempty . consumed)++allOccuring :: Occurences -> Names+allOccuring occs = allConsumed occs <> allObserved occs++anyConsumption :: Occurences -> Maybe Occurence+anyConsumption = find (isJust . consumed)++seqOccurences :: Occurences -> Occurences -> Occurences+seqOccurences occurs1 occurs2 =+  filter (not . nullOccurence) $ map filt occurs1 ++ occurs2+  where filt occ =+          occ { observed = observed occ `S.difference` postcons }+        postcons = allConsumed occurs2++altOccurences :: Occurences -> Occurences -> Occurences+altOccurences occurs1 occurs2 =+  filter (not . nullOccurence) $ map filt1 occurs1 ++ map filt2 occurs2+  where filt1 occ =+          occ { consumed = S.difference <$> consumed occ <*> pure cons2+              , observed = observed occ `S.difference` cons2 }+        filt2 occ =+          occ { consumed = consumed occ+              , observed = observed occ `S.difference` cons1 }+        cons1 = allConsumed occurs1+        cons2 = allConsumed occurs2++--- Scope management++data Checking+  = CheckingApply (Maybe (QualName VName)) Exp StructType StructType+  | CheckingReturn StructType StructType+  | CheckingAscription StructType StructType+  | CheckingLetGeneralise Name+  | CheckingParams (Maybe Name)+  | CheckingPattern UncheckedPattern InferredType+  | CheckingLoopBody StructType StructType+  | CheckingLoopInitial StructType StructType+  | CheckingRecordUpdate [Name] StructType StructType+  | CheckingRequired [StructType] StructType+  | CheckingBranches StructType StructType++instance Pretty Checking where+  ppr (CheckingApply f e expected actual) =+    header </>+    "Expected:" <+> align (ppr expected) </>+    "Actual:  " <+> align (ppr actual)+    where header =+            case f of+              Nothing ->+                "Cannot apply function to" <+>+                pquote (shorten $ pretty $ flatten $ ppr e) <> " (invalid type)."+              Just fname ->+                "Cannot apply" <+> pquote (ppr fname) <+> "to" <+>+                pquote (shorten $ pretty $ flatten $ ppr e) <> " (invalid type)."++  ppr (CheckingReturn expected actual) =+    "Function body does not have expected type." </>+    "Expected:" <+> align (ppr expected) </>+    "Actual:  " <+> align (ppr actual)++  ppr (CheckingAscription expected actual) =+    "Expression does not have expected type from explicit ascription." </>+    "Expected:" <+> align (ppr expected) </>+    "Actual:  " <+> align (ppr actual)++  ppr (CheckingLetGeneralise fname) =+    "Cannot generalise type of" <+> pquote (ppr fname) <> "."++  ppr (CheckingParams fname) =+    "Invalid use of parameters in" <+> pquote fname' <> "."+    where fname' = maybe "anonymous function" ppr fname++  ppr (CheckingPattern pat NoneInferred) =+    "Invalid pattern" <+> pquote (ppr pat) <> "."++  ppr (CheckingPattern pat (Ascribed t)) =+    "Pattern" <+> pquote (ppr pat) <+>+    "cannot match value of type" </>+    indent 2 (ppr t)++  ppr (CheckingLoopBody expected actual) =+    "Loop body does not have expected type." </>+    "Expected:" <+> align (ppr expected) </>+    "Actual:  " <+> align (ppr actual)++  ppr (CheckingLoopInitial expected actual) =+    "Initial loop values do not have expected type." </>+    "Expected:" <+> align (ppr expected) </>+    "Actual:  " <+> align (ppr actual)++  ppr (CheckingRecordUpdate fs expected actual) =+    "Type mismatch when updating record field" <+> pquote fs' <> "." </>+    "Existing:" <+> align (ppr expected) </>+    "New:     " <+> align (ppr actual)+    where fs' = mconcat $ punctuate "." $ map ppr fs++  ppr (CheckingRequired [expected] actual) =+    "Expression must must have type" <+> ppr expected <> "." </>+    "Actual type:" <+> align (ppr actual)++  ppr (CheckingRequired expected actual) =+    "Type of expression must must be one of " <+> expected' <> "." </>+    "Actual type:" <+> align (ppr actual)+    where expected' = commasep (map ppr expected)++  ppr (CheckingBranches t1 t2) =+    "Conditional branches differ in type." </>+    "Former:" <+> ppr t1 </>+    "Latter:" <+> ppr t2++-- | Whether something is a global or a local variable.+data Locality = Local | Global+              deriving (Show)++data ValBinding = BoundV Locality [TypeParam] PatternType+                -- ^ Aliases in parameters indicate the lexical+                -- closure.+                | OverloadedF [PrimType] [Maybe PrimType] (Maybe PrimType)+                | EqualityF+                | WasConsumed SrcLoc+                deriving (Show)++-- | Type checking happens with access to this environment.  The+-- 'TermScope' will be extended during type-checking as bindings come into+-- scope.+data TermEnv = TermEnv { termScope :: TermScope+                       , termChecking :: Maybe Checking+                       , termLevel :: Level+                       }++data TermScope = TermScope { scopeVtable  :: M.Map VName ValBinding+                           , scopeTypeTable :: M.Map VName TypeBinding+                           , scopeModTable :: M.Map VName Mod+                           , scopeNameMap :: NameMap+                           } deriving (Show)++instance Semigroup TermScope where+  TermScope vt1 tt1 mt1 nt1 <> TermScope vt2 tt2 mt2 nt2 =+    TermScope (vt2 `M.union` vt1) (tt2 `M.union` tt1) (mt1 `M.union` mt2) (nt2 `M.union` nt1)++envToTermScope :: Env -> TermScope+envToTermScope env = TermScope { scopeVtable = vtable+                               , scopeTypeTable = envTypeTable env+                               , scopeNameMap = envNameMap env+                               , scopeModTable = envModTable env+                               }+  where vtable = M.mapWithKey valBinding $ envVtable env+        valBinding k (TypeM.BoundV tps v) =+          BoundV Global tps $ v `setAliases`+          (if arrayRank v > 0 then S.singleton (AliasBound k) else mempty)++withEnv :: TermEnv -> Env -> TermEnv+withEnv tenv env = tenv { termScope = termScope tenv <> envToTermScope env }++overloadedTypeVars :: Constraints -> Names+overloadedTypeVars = mconcat . map f . M.elems+  where f (_, HasFields fs _) = mconcat $ map typeVars $ M.elems fs+        f _ = mempty++-- | Get the type of an expression, with top level type variables+-- substituted.  Never call 'typeOf' directly (except in a few+-- carefully inspected locations)!+expType :: Exp -> TermTypeM PatternType+expType = normPatternType . typeOf++-- | Get the type of an expression, with all type variables+-- substituted.  Slower than 'expType', but sometimes necessary.+-- Never call 'typeOf' directly (except in a few carefully inspected+-- locations)!+expTypeFully :: Exp -> TermTypeM PatternType+expTypeFully = normTypeFully . typeOf++-- Wrap a function name to give it a vacuous Eq instance for SizeSource.+newtype FName = FName (Maybe (QualName VName))+              deriving (Show)++instance Eq FName where+  _ == _ = True++instance Ord FName where+  compare _ _ = EQ++-- | What was the source of some existential size?  This is used for+-- using the same existential variable if the same source is+-- encountered in multiple locations.+data SizeSource = SourceArg FName (ExpBase NoInfo VName)+                | SourceBound (ExpBase NoInfo VName)+                | SourceSlice+                  (Maybe (DimDecl VName))+                  (Maybe (ExpBase NoInfo VName))+                  (Maybe (ExpBase NoInfo VName))+                  (Maybe (ExpBase NoInfo VName))+                deriving (Eq, Ord, Show)++-- | The state is a set of constraints and a counter for generating+-- type names.  This is distinct from the usual counter we use for+-- generating unique names, as these will be user-visible.+data TermTypeState = TermTypeState+                     { stateConstraints :: Constraints+                     , stateCounter :: !Int+                     , stateDimTable :: M.Map SizeSource VName+                       -- ^ Mapping function arguments encountered to+                       -- the sizes they ended up generating (when+                       -- they could not be substituted directly).+                       -- This happens for function arguments that are+                       -- not constants or names.+                     }++newtype TermTypeM a = TermTypeM (RWST+                                 TermEnv+                                 Occurences+                                 TermTypeState+                                 TypeM+                                 a)+  deriving (Monad, Functor, Applicative,+            MonadReader TermEnv,+            MonadWriter Occurences,+            MonadState TermTypeState,+            MonadError TypeError)++instance MonadUnify TermTypeM where+  getConstraints = gets stateConstraints+  putConstraints x = modify $ \s -> s { stateConstraints = x }++  newTypeVar loc desc = do+    i <- incCounter+    v <- newID $ mkTypeVarName desc i+    constrain v $ NoConstraint Lifted $ mkUsage' loc+    return $ Scalar $ TypeVar mempty Nonunique (typeName v) []++  curLevel = asks termLevel++  newDimVar loc rigidity name = do+    i <- incCounter+    dim <- newID $ mkTypeVarName name i+    case rigidity of+      Rigid rsrc -> constrain dim $ UnknowableSize loc rsrc+      Nonrigid -> constrain dim $ Size Nothing $ mkUsage' loc+    return dim++  unifyError loc notes bcs doc = do+    checking <- asks termChecking+    case checking of+      Just checking' ->+        throwError $ TypeError (srclocOf loc) notes $+        ppr checking' <> line </> doc <> ppr bcs+      Nothing ->+        throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs++  matchError loc notes bcs t1 t2 = do+    checking <- asks termChecking+    case checking of+      Just checking'+        | hasNoBreadCrumbs bcs ->+            throwError $ TypeError (srclocOf loc) notes $+            ppr checking'+        | otherwise ->+            throwError $ TypeError (srclocOf loc) notes $+            ppr checking' <> line </> doc <> ppr bcs+      Nothing ->+        throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs+    where doc = "Types" </>+                indent 2 (ppr t1) </>+                "and" </>+                indent 2 (ppr t2) </>+                "do not match."++onFailure :: Checking -> TermTypeM a -> TermTypeM a+onFailure c = local $ \env -> env { termChecking = Just c }++runTermTypeM :: TermTypeM a -> TypeM (a, Occurences)+runTermTypeM (TermTypeM m) = do+  initial_scope <- (initialTermScope <>) . envToTermScope <$> askEnv+  let initial_tenv = TermEnv { termScope = initial_scope+                             , termChecking = Nothing+                             , termLevel = 0+                             }+  evalRWST m initial_tenv $ TermTypeState mempty 0 mempty++liftTypeM :: TypeM a -> TermTypeM a+liftTypeM = TermTypeM . lift++localScope :: (TermScope -> TermScope) -> TermTypeM a -> TermTypeM a+localScope f = local $ \tenv -> tenv { termScope = f $ termScope tenv }++incCounter :: TermTypeM Int+incCounter = do s <- get+                put s { stateCounter = stateCounter s + 1 }+                return $ stateCounter s++extSize :: SrcLoc -> SizeSource -> TermTypeM (DimDecl VName, Maybe VName)+extSize loc e = do+  prev <- gets $ M.lookup e . stateDimTable+  case prev of+    Nothing -> do+      let rsrc = case e of+                   SourceArg (FName fname) e' ->+                     RigidArg fname $ prettyOneLine e'+                   SourceBound e' ->+                     RigidBound $ prettyOneLine e'+                   SourceSlice d i j s  ->+                     RigidSlice d $ prettyOneLine $ DimSlice i j s+      d <- newDimVar loc (Rigid rsrc) "argdim"+      modify $ \s -> s { stateDimTable = M.insert e d $ stateDimTable s }+      return (NamedDim $ qualName d,+              Just d)+    Just d -> return (NamedDim $ qualName d,+                      Nothing)++-- Any argument sizes created with 'extSize' inside the given action+-- will be removed once the action finishes.  This is to ensure that+-- just because e.g. @n+1@ appears as a size in one branch of a+-- conditional, that doesn't mean it's also available in the other branch.+noSizeEscape :: TermTypeM a -> TermTypeM a+noSizeEscape m = do+  dimtable <- gets stateDimTable+  x <- m+  modify $ \s -> s { stateDimTable = dimtable }+  return x++constrain :: VName -> Constraint -> TermTypeM ()+constrain v c = do+  lvl <- curLevel+  modifyConstraints $ M.insert v (lvl, c)++incLevel :: TermTypeM a -> TermTypeM a+incLevel = local $ \env -> env { termLevel = termLevel env + 1 }++initialTermScope :: TermScope+initialTermScope = TermScope { scopeVtable = initialVtable+                             , scopeTypeTable = mempty+                             , scopeNameMap = topLevelNameMap+                             , scopeModTable = mempty+                             }+  where initialVtable = M.fromList $ mapMaybe addIntrinsicF $ M.toList intrinsics++        prim = Scalar . Prim+        arrow x y = Scalar $ Arrow mempty Unnamed x y++        addIntrinsicF (name, IntrinsicMonoFun pts t) =+          Just (name, BoundV Global [] $ arrow pts' $ prim t)+          where pts' = case pts of [pt] -> prim pt+                                   _    -> tupleRecord $ map prim pts++        addIntrinsicF (name, IntrinsicOverloadedFun ts pts rts) =+          Just (name, OverloadedF ts pts rts)+        addIntrinsicF (name, IntrinsicPolyFun tvs pts rt) =+          Just (name, BoundV Global tvs $+                      fromStruct $ Scalar $ Arrow mempty Unnamed pts' rt)+          where pts' = case pts of [pt] -> pt+                                   _    -> tupleRecord pts+        addIntrinsicF (name, IntrinsicEquality) =+          Just (name, EqualityF)+        addIntrinsicF _ = Nothing++instance MonadTypeChecker TermTypeM where+  warn loc problem = liftTypeM $ warn loc problem+  newName = liftTypeM . newName+  newID = liftTypeM . newID++  checkQualName space name loc = snd <$> checkQualNameWithEnv space name loc++  bindNameMap m = localScope $ \scope ->+    scope { scopeNameMap = m <> scopeNameMap scope }++  bindVal v (TypeM.BoundV tps t) = localScope $ \scope ->+    scope { scopeVtable = M.insert v vb $ scopeVtable scope }+    where vb = BoundV Local tps $ fromStruct t++  lookupType loc qn = do+    outer_env <- liftTypeM askEnv+    (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Type qn loc+    case M.lookup name $ scopeTypeTable scope of+      Nothing -> undefinedType loc qn+      Just (TypeAbbr l ps def) ->+        return (qn', ps, qualifyTypeVars outer_env (map typeParamName ps) qs def, l)++  lookupMod loc qn = do+    (scope, qn'@(QualName _ name)) <- checkQualNameWithEnv Term qn loc+    case M.lookup name $ scopeModTable scope of+      Nothing -> unknownVariableError Term qn loc+      Just m  -> return (qn', m)++  lookupVar loc qn = do+    outer_env <- liftTypeM askEnv+    (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Term qn loc+    let usage = mkUsage loc $ "use of " ++ quote (pretty qn)++    t <- case M.lookup name $ scopeVtable scope of+      Nothing -> typeError loc mempty $+                 "Unknown variable" <+> pquote (ppr qn) <> "."++      Just (WasConsumed wloc) -> useAfterConsume (baseName name) loc wloc++      Just (BoundV _ tparams t)+        | "_" `isPrefixOf` baseString name -> underscoreUse loc qn+        | otherwise -> do+            (tnames, t') <- instantiateTypeScheme loc tparams t+            return $ qualifyTypeVars outer_env tnames qs t'++      Just EqualityF -> do+        argtype <- newTypeVar loc "t"+        equalityType usage argtype+        return $+          Scalar $ Arrow mempty Unnamed argtype $+          Scalar $ Arrow mempty Unnamed argtype $ Scalar $ Prim Bool++      Just (OverloadedF ts pts rt) -> do+        argtype <- newTypeVar loc "t"+        mustBeOneOf ts usage argtype+        let (pts', rt') = instOverloaded argtype pts rt+            arrow xt yt = Scalar $ Arrow mempty Unnamed xt yt+        return $ fromStruct $ foldr arrow rt' pts'++    observe $ Ident name (Info t) loc+    return (qn', t)++      where instOverloaded argtype pts rt =+              (map (maybe (toStruct argtype) (Scalar . Prim)) pts,+               maybe (toStruct argtype) (Scalar . Prim) rt)++  checkNamedDim loc v = do+    (v', t) <- lookupVar loc v+    onFailure (CheckingRequired [Scalar $ Prim $ Signed Int32] (toStruct t)) $+      unify (mkUsage loc "use as array size") (toStruct t) $+      Scalar $ Prim $ Signed Int32+    return v'++  typeError loc notes s = do+    checking <- asks termChecking+    case checking of+      Just checking' ->+        throwError $ TypeError (srclocOf loc) notes (ppr checking' <> line </> s)+      Nothing ->+        throwError $ TypeError (srclocOf loc) notes s++checkQualNameWithEnv :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName)+checkQualNameWithEnv space qn@(QualName quals name) loc = do+  scope <- asks termScope+  descend scope quals+  where descend scope []+          | Just name' <- M.lookup (space, name) $ scopeNameMap scope =+              return (scope, name')+          | otherwise =+              unknownVariableError space qn loc++        descend scope (q:qs)+          | Just (QualName _ q') <- M.lookup (Term, q) $ scopeNameMap scope,+            Just res <- M.lookup q' $ scopeModTable scope =+              case res of+                -- Check if we are referring to the magical intrinsics+                -- module.+                _ | baseTag q' <= maxIntrinsicTag ->+                      checkIntrinsic space qn loc+                ModEnv q_scope -> do+                  (scope', QualName qs' name') <- descend (envToTermScope q_scope) qs+                  return (scope', QualName (q':qs') name')+                ModFun{} -> unappliedFunctor loc+          | otherwise =+              unknownVariableError space qn loc++checkIntrinsic :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName)+checkIntrinsic space qn@(QualName _ name) loc+  | Just v <- M.lookup (space, name) intrinsicsNameMap = do+      me <- liftTypeM askImportName+      unless ("/prelude" `isPrefixOf` includeToString me) $+        warn loc "Using intrinsic functions directly can easily crash the compiler or result in wrong code generation."+      scope <- asks termScope+      return (scope, v)+  | otherwise =+      unknownVariableError space qn loc++-- | Wrap 'Types.checkTypeDecl' to also perform an observation of+-- every size in the type.+checkTypeDecl :: TypeDeclBase NoInfo Name -> TermTypeM (TypeDeclBase Info VName)+checkTypeDecl tdecl = do+  (tdecl', _) <- Types.checkTypeDecl tdecl+  mapM_ observeDim $ nestedDims $ unInfo $ expandedType tdecl'+  return tdecl'+  where observeDim (NamedDim v) =+          observe $ Ident (qualLeaf v) (Info $ Scalar $ Prim $ Signed Int32) noLoc+        observeDim _ = return ()++-- | Instantiate a type scheme with fresh type variables for its type+-- parameters. Returns the names of the fresh type variables, the+-- instance list, and the instantiated type.+instantiateTypeScheme :: SrcLoc -> [TypeParam] -> PatternType+                      -> TermTypeM ([VName], PatternType)+instantiateTypeScheme loc tparams t = do+  let tnames = map typeParamName tparams+  (tparam_names, tparam_substs) <- unzip <$> mapM (instantiateTypeParam loc) tparams+  let substs = M.fromList $ zip tnames tparam_substs+      t' = applySubst (`M.lookup` substs) t+  return (tparam_names, t')++-- | Create a new type name and insert it (unconstrained) in the+-- substitution map.+instantiateTypeParam :: Monoid as => SrcLoc -> TypeParam -> TermTypeM (VName, Subst (TypeBase dim as))+instantiateTypeParam loc tparam = do+  i <- incCounter+  v <- newID $ mkTypeVarName (takeWhile isAscii (baseString (typeParamName tparam))) i+  case tparam of TypeParamType x _ _ -> do+                   constrain v $ NoConstraint x $ mkUsage' loc+                   return (v, Subst $ Scalar $ TypeVar mempty Nonunique (typeName v) [])+                 TypeParamDim{} -> do+                   constrain v $ Size Nothing $ mkUsage' loc+                   return (v, SizeSubst $ NamedDim $ qualName v)++newArrayType :: SrcLoc -> String -> Int -> TermTypeM (StructType, StructType)+newArrayType loc desc r = do+  v <- newID $ nameFromString desc+  constrain v $ NoConstraint Unlifted $ mkUsage' loc+  dims <- replicateM r $ newDimVar loc Nonrigid "dim"+  let rowt = TypeVar () Nonunique (typeName v) []+  return (Array () Nonunique rowt (ShapeDecl $ map (NamedDim . qualName) dims),+          Scalar rowt)++--- Errors++useAfterConsume :: Name -> SrcLoc -> SrcLoc -> TermTypeM a+useAfterConsume name rloc wloc =+  typeError rloc mempty $+  "Variable" <+> pquote (pprName name) <+> "previously consumed at" <+>+  text (locStrRel rloc wloc) <> ".  (Possibly through aliasing.)"++consumeAfterConsume :: Name -> SrcLoc -> SrcLoc -> TermTypeM a+consumeAfterConsume name loc1 loc2 =+  typeError loc2 mempty $+  "Variable" <+> pprName name <+> "previously consumed at" <+>+  text (locStrRel loc2 loc1) <> "."++badLetWithValue :: SrcLoc -> TermTypeM a+badLetWithValue loc =+  typeError loc mempty+  "New value for elements in let-with shares data with source array.  This is illegal, as it prevents in-place modification."++returnAliased :: Name -> Name -> SrcLoc -> TermTypeM ()+returnAliased fname name loc =+  typeError loc mempty $+  "Unique return value of" <+> pquote (pprName fname) <+>+  "is aliased to" <+> pquote (pprName name) <+> ", which is not consumed."++uniqueReturnAliased :: Name -> SrcLoc -> TermTypeM a+uniqueReturnAliased fname loc =+  typeError loc mempty $+  "A unique tuple element of return value of" <+>+  pquote (pprName fname) <+> "is aliased to some other tuple component."++--- Basic checking++-- | Determine if the two types of identical, ignoring uniqueness.+-- Mismatched dimensions are turned into fresh rigid type variables.+-- Causes a 'TypeError' if they fail to match, and otherwise returns+-- one of them.+unifyBranchTypes :: SrcLoc -> PatternType -> PatternType -> TermTypeM (PatternType, [VName])+unifyBranchTypes loc t1 t2 =+  onFailure (CheckingBranches (toStruct t1) (toStruct t2)) $+  unifyMostCommon (mkUsage loc "unification of branch results") t1 t2++unifyBranches :: SrcLoc -> Exp -> Exp -> TermTypeM (PatternType, [VName])+unifyBranches loc e1 e2 = do+  e1_t <- expTypeFully e1+  e2_t <- expTypeFully e2+  unifyBranchTypes loc e1_t e2_t++--- General binding.++doNotShadow :: [String]+doNotShadow = ["&&", "||"]++data InferredType = NoneInferred+                  | Ascribed PatternType+++checkPattern' :: UncheckedPattern -> InferredType+              -> TermTypeM Pattern++checkPattern' (PatternParens p loc) t =+  PatternParens <$> checkPattern' p t <*> pure loc++checkPattern' (Id name _ loc) _+  | name' `elem` doNotShadow =+      typeError loc mempty $ "The" <+> text name' <+> "operator may not be redefined."+  where name' = nameToString name++checkPattern' (Id name NoInfo loc) (Ascribed t) = do+  name' <- newID name+  return $ Id name' (Info t) loc+checkPattern' (Id name NoInfo loc) NoneInferred = do+  name' <- newID name+  t <- newTypeVar loc "t"+  return $ Id name' (Info t) loc++checkPattern' (Wildcard _ loc) (Ascribed t) =+  return $ Wildcard (Info $ t `setUniqueness` Nonunique) loc+checkPattern' (Wildcard NoInfo loc) NoneInferred = do+  t <- newTypeVar loc "t"+  return $ Wildcard (Info t) loc++checkPattern' (TuplePattern ps loc) (Ascribed t)+  | Just ts <- isTupleRecord t, length ts == length ps =+      TuplePattern <$> zipWithM checkPattern' ps (map Ascribed ts) <*> pure loc+checkPattern' p@(TuplePattern ps loc) (Ascribed t) = do+  ps_t <- replicateM (length ps) (newTypeVar loc "t")+  unify (mkUsage loc "matching a tuple pattern") (tupleRecord ps_t) $ toStruct t+  t' <- normTypeFully t+  checkPattern' p $ Ascribed t'+checkPattern' (TuplePattern ps loc) NoneInferred =+  TuplePattern <$> mapM (`checkPattern'` NoneInferred) ps <*> pure loc++checkPattern' (RecordPattern p_fs _) _+  | Just (f, fp) <- find (("_" `isPrefixOf`) . nameToString . fst) p_fs =+      typeError fp mempty $+      "Underscore-prefixed fields are not allowed." </>+      "Did you mean" <> dquotes (text (drop 1 (nameToString f)) <> "=_") <> "?"++checkPattern' (RecordPattern p_fs loc) (Ascribed (Scalar (Record t_fs)))+  | sort (map fst p_fs) == sort (M.keys t_fs) =+    RecordPattern . M.toList <$> check <*> pure loc+    where check = traverse (uncurry checkPattern') $ M.intersectionWith (,)+                  (M.fromList p_fs) (fmap Ascribed t_fs)+checkPattern' p@(RecordPattern fields loc) (Ascribed t) = do+  fields' <- traverse (const $ newTypeVar loc "t") $ M.fromList fields++  when (sort (M.keys fields') /= sort (map fst fields)) $+    typeError loc mempty $ "Duplicate fields in record pattern" <+> ppr p <> "."++  unify (mkUsage loc "matching a record pattern") (Scalar (Record fields')) $ toStruct t+  t' <- normTypeFully t+  checkPattern' p $ Ascribed t'+checkPattern' (RecordPattern fs loc) NoneInferred =+  RecordPattern . M.toList <$> traverse (`checkPattern'` NoneInferred) (M.fromList fs) <*> pure loc++checkPattern' (PatternAscription p (TypeDecl t NoInfo) loc) maybe_outer_t = do+  (t', st_nodims, _) <- checkTypeExp t+  (st, _) <- instantiateEmptyArrayDims loc "impl" Nonrigid st_nodims++  let st' = fromStruct st+  case maybe_outer_t of+    Ascribed outer_t -> do+      unify (mkUsage loc "explicit type ascription") (toStruct st) (toStruct outer_t)++      -- We also have to make sure that uniqueness matches.  This is+      -- done explicitly, because it is ignored by unification.+      st'' <- normTypeFully st'+      outer_t' <- normTypeFully outer_t+      case unifyTypesU unifyUniqueness st'' outer_t' of+        Just outer_t'' ->+          PatternAscription <$> checkPattern' p (Ascribed outer_t'') <*>+          pure (TypeDecl t' (Info st)) <*> pure loc+        Nothing ->+          typeError loc mempty $+          "Cannot match type" <+> pquote (ppr outer_t') <+> "with expected type" <+>+          pquote (ppr st'') <> "."++    NoneInferred ->+      PatternAscription <$> checkPattern' p (Ascribed st') <*>+      pure (TypeDecl t' (Info st)) <*> pure loc+ where unifyUniqueness u1 u2 = if u2 `subuniqueOf` u1 then Just u1 else Nothing++checkPattern' (PatternLit e NoInfo loc) (Ascribed t) = do+  e' <- checkExp e+  t' <- expTypeFully e'+  unify (mkUsage loc "matching against literal") (toStruct t') (toStruct t)+  return $ PatternLit e' (Info t') loc++checkPattern' (PatternLit e NoInfo loc) NoneInferred = do+  e' <- checkExp e+  t' <- expTypeFully e'+  return $ PatternLit e' (Info t') loc++checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed (Scalar (Sum cs)))+  | Just ts <- M.lookup n cs = do+      ps' <- zipWithM checkPattern' ps $ map Ascribed ts+      return $ PatternConstr n (Info (Scalar (Sum cs))) ps' loc++checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed t) = do+  t' <- newTypeVar loc "t"+  ps' <- mapM (`checkPattern'` NoneInferred) ps+  mustHaveConstr usage n t' (patternStructType <$> ps')+  unify usage t' (toStruct t)+  t'' <- normTypeFully t+  return $ PatternConstr n (Info t'') ps' loc+  where usage = mkUsage loc "matching against constructor"++checkPattern' (PatternConstr n NoInfo ps loc) NoneInferred = do+  ps' <- mapM (`checkPattern'` NoneInferred) ps+  t <- newTypeVar loc "t"+  mustHaveConstr usage n t (patternStructType <$> ps')+  return $ PatternConstr n (Info $ fromStruct t) ps' loc+  where usage = mkUsage loc "matching against constructor"++patternNameMap :: Pattern -> NameMap+patternNameMap = M.fromList . map asTerm . S.toList . patternNames+  where asTerm v = ((Term, baseName v), qualName v)++checkPattern :: UncheckedPattern -> InferredType -> (Pattern -> TermTypeM a)+             -> TermTypeM a+checkPattern p t m = do+  checkForDuplicateNames [p]+  p' <- onFailure (CheckingPattern p t) $ checkPattern' p t+  bindNameMap (patternNameMap p') $ m p'++binding :: [Ident] -> TermTypeM a -> TermTypeM a+binding bnds = check . handleVars+  where handleVars m =+          localScope (`bindVars` bnds) $ do++          -- Those identifiers that can potentially also be sizes are+          -- added as type constraints.  This is necessary so that we+          -- can properly detect scope violations during unification.+          -- We do this for *all* identifiers, not just those that are+          -- integers, because they may become integers later due to+          -- inference...+          forM_ bnds $ \ident ->+            constrain (identName ident) $ ParamSize $ srclocOf ident+          m++        bindVars :: TermScope -> [Ident] -> TermScope+        bindVars = foldl bindVar++        bindVar :: TermScope -> Ident -> TermScope+        bindVar scope (Ident name (Info tp) _) =+          let inedges = boundAliases $ aliases tp+              update (BoundV l tparams in_t)+                -- If 'name' is record or sum-typed, don't alias the+                -- components to 'name', because these no identity+                -- beyond their components.+                | Array{} <- tp = BoundV l tparams (in_t `addAliases` S.insert (AliasBound name))+                | otherwise = BoundV l tparams in_t+              update b = b++              tp' = tp `addAliases` S.insert (AliasBound name)+          in scope { scopeVtable = M.insert name (BoundV Local [] tp') $+                                   adjustSeveral update inedges $+                                   scopeVtable scope+                   }++        adjustSeveral f = flip $ foldl $ flip $ M.adjust f++        -- Check whether the bound variables have been used correctly+        -- within their scope.+        check m = do+          (a, usages) <- collectBindingsOccurences m+          checkOccurences usages++          mapM_ (checkIfUsed usages) bnds++          return a++        -- Collect and remove all occurences in @bnds@.  This relies+        -- on the fact that no variables shadow any other.+        collectBindingsOccurences m = pass $ do+          (x, usage) <- listen m+          let (relevant, rest) = split usage+          return ((x, relevant), const rest)+          where split = unzip .+                        map (\occ ->+                             let (obs1, obs2) = divide $ observed occ+                                 occ_cons = divide <$> consumed occ+                                 con1 = fst <$> occ_cons+                                 con2 = snd <$> occ_cons+                             in (occ { observed = obs1, consumed = con1 },+                                 occ { observed = obs2, consumed = con2 }))+                names = S.fromList $ map identName bnds+                divide s = (s `S.intersection` names, s `S.difference` names)++bindingTypes :: [Either (VName, TypeBinding) (VName, Constraint)]+             -> TermTypeM a -> TermTypeM a+bindingTypes types m = do+  lvl <- curLevel+  modifyConstraints (<>M.map (lvl,) (M.fromList constraints))+  localScope extend m+  where (tbinds, constraints) = partitionEithers types+        extend scope = scope {+          scopeTypeTable = M.fromList tbinds <> scopeTypeTable scope+          }++bindingTypeParams :: [TypeParam] -> TermTypeM a -> TermTypeM a+bindingTypeParams tparams = binding (mapMaybe typeParamIdent tparams) .+                            bindingTypes (concatMap typeParamType tparams)+  where typeParamType (TypeParamType l v loc) =+          [ Left (v, TypeAbbr l [] (Scalar (TypeVar () Nonunique (typeName v) [])))+          , Right (v, ParamType l loc) ]+        typeParamType (TypeParamDim v loc) =+          [ Right (v, ParamSize loc) ]++typeParamIdent :: TypeParam -> Maybe Ident+typeParamIdent (TypeParamDim v loc) =+  Just $ Ident v (Info $ Scalar $ Prim $ Signed Int32) loc+typeParamIdent _ = Nothing++bindingIdent :: IdentBase NoInfo Name -> PatternType -> (Ident -> TermTypeM a)+             -> TermTypeM a+bindingIdent (Ident v NoInfo vloc) t m =+  bindSpaced [(Term, v)] $ do+    v' <- checkName Term v vloc+    let ident = Ident v' (Info t) vloc+    binding [ident] $ m ident++bindingParams :: [UncheckedTypeParam]+              -> [UncheckedPattern]+              -> ([TypeParam] -> [Pattern] -> TermTypeM a) -> TermTypeM a+bindingParams tps orig_ps m = do+  checkForDuplicateNames orig_ps+  checkTypeParams tps $ \tps' -> bindingTypeParams tps' $ do+    let descend ps' (p:ps) =+          checkPattern p NoneInferred $ \p' ->+            binding (S.toList $ patternIdents p') $ descend (p':ps') ps+        descend ps' [] = do+          -- Perform an observation of every type parameter.  This+          -- prevents unused-name warnings for otherwise unused+          -- dimensions.+          mapM_ observe $ mapMaybe typeParamIdent tps'+          m tps' $ reverse ps'++    descend [] orig_ps++bindingPattern :: PatternBase NoInfo Name -> InferredType+               -> (Pattern -> TermTypeM a) -> TermTypeM a+bindingPattern p t m = do+  checkForDuplicateNames [p]+  checkPattern p t $ \p' -> binding (S.toList $ patternIdents p') $ do+    -- Perform an observation of every declared dimension.  This+    -- prevents unused-name warnings for otherwise unused dimensions.+    mapM_ observe $ patternDims p'++    m p'++patternDims :: Pattern -> [Ident]+patternDims (PatternParens p _) = patternDims p+patternDims (TuplePattern pats _) = concatMap patternDims pats+patternDims (PatternAscription p (TypeDecl _ (Info t)) _) =+  patternDims p <> mapMaybe (dimIdent (srclocOf p)) (nestedDims t)+  where dimIdent _ AnyDim            = Nothing+        dimIdent _ (ConstDim _)      = Nothing+        dimIdent _ NamedDim{}        = Nothing+patternDims _ = []++sliceShape :: Maybe (SrcLoc, Rigidity) -> [DimIndex] -> TypeBase (DimDecl VName) as+           -> TermTypeM (TypeBase (DimDecl VName) as, [VName])+sliceShape r slice t@(Array als u et (ShapeDecl orig_dims)) =+  runWriterT $ setDims <$> adjustDims slice orig_dims+  where setDims []    = stripArray (length orig_dims) t+        setDims dims' = Array als u et $ ShapeDecl dims'++        -- If the result is supposed to be AnyDim or a nonrigid size+        -- variable, then don't bother trying to create+        -- non-existential sizes.  This is necessary to make programs+        -- type-check without too much ceremony; see+        -- e.g. tests/inplace5.fut.+        isRigid Rigid{} = True+        isRigid _ = False+        refine_sizes = maybe False (isRigid . snd) r++        sliceSize orig_d i j stride =+          case r of+            Just (loc, Rigid _) -> do+              (d, ext) <-+                lift $ extSize loc $+                SourceSlice orig_d' (bareExp <$> i) (bareExp <$> j) (bareExp <$> stride)+              tell $ maybeToList ext+              return d+            Just (loc, Nonrigid) ->+              lift $ NamedDim . qualName <$> newDimVar loc Nonrigid "slice_dim"+            Nothing ->+              pure AnyDim+          where+            -- The original size does not matter if the slice is fully specified.+            orig_d' | isJust i, isJust j = Nothing+                    | otherwise = Just orig_d++        adjustDims (DimFix{} : idxes') (_:dims) =+          adjustDims idxes' dims++        -- Pattern match some known slices to be non-existential.+        adjustDims (DimSlice i j stride : idxes') (_:dims)+          | refine_sizes,+            maybe True ((==Just 0) . isInt32) i,+            Just j' <- maybeDimFromExp =<< j,+            maybe True ((==Just 1) . isInt32) stride =+              (j':) <$> adjustDims idxes' dims++        adjustDims (DimSlice Nothing Nothing stride : idxes') (d:dims)+          | refine_sizes,+            maybe True (maybe False ((==1) . abs) . isInt32) stride =+              (d:) <$> adjustDims idxes' dims++        adjustDims (DimSlice i j stride : idxes') (d:dims) =+          (:) <$> sliceSize d i j stride <*> adjustDims idxes' dims++        adjustDims _ dims =+          pure dims++sliceShape _ _ t = pure (t, [])++--- Main checkers++-- | @require ts e@ causes a 'TypeError' if @expType e@ is not one of+-- the types in @ts@.  Otherwise, simply returns @e@.+require :: String -> [PrimType] -> Exp -> TermTypeM Exp+require why ts e = do mustBeOneOf ts (mkUsage (srclocOf e) why) . toStruct =<< expType e+                      return e++unifies :: String -> StructType -> Exp -> TermTypeM Exp+unifies why t e = do+  unify (mkUsage (srclocOf e) why) t =<< toStruct <$> expType e+  return e++-- The closure of a lambda or local function are those variables that+-- it references, and which local to the current top-level function.+lexicalClosure :: [Pattern] -> Occurences -> TermTypeM Aliasing+lexicalClosure params closure = do+  vtable <- asks $ scopeVtable . termScope+  let isLocal v = case v `M.lookup` vtable of+                    Just (BoundV Local _ _) -> True+                    _ -> False+  return $ S.map AliasBound $ S.filter isLocal $+    allOccuring closure S.\\ mconcat (map patternNames params)++noAliasesIfOverloaded :: PatternType -> TermTypeM PatternType+noAliasesIfOverloaded t@(Scalar (TypeVar _ u tn [])) = do+  subst <- fmap snd . M.lookup (typeLeaf tn) <$> getConstraints+  case subst of+    Just Overloaded{} -> return $ Scalar $ TypeVar mempty u tn []+    _ -> return t+noAliasesIfOverloaded t =+  return t++-- Check the common parts of ascription and coercion.+checkAscript :: SrcLoc+             -> UncheckedTypeDecl+             -> UncheckedExp+             -> (StructType -> StructType)+             -> TermTypeM (TypeDecl, Exp)+checkAscript loc decl e shapef = do+  decl' <- checkTypeDecl decl+  e' <- checkExp e+  t <- expTypeFully e'++  (decl_t_nonrigid, _) <-+    instantiateEmptyArrayDims loc "impl" Nonrigid $ shapef $+    unInfo $ expandedType decl'++  onFailure (CheckingAscription (unInfo $ expandedType decl') (toStruct t)) $+    unify (mkUsage loc "type ascription") decl_t_nonrigid (toStruct t)++  -- We also have to make sure that uniqueness matches.  This is done+  -- explicitly, because uniqueness is ignored by unification.+  t' <- normTypeFully t+  decl_t' <- normTypeFully $ unInfo $ expandedType decl'+  unless (t' `subtypeOf` anySizes decl_t') $+    typeError loc mempty $ "Type" <+> pquote (ppr t') <+> "is not a subtype of" <+>+    pquote (ppr decl_t') <> "."++  return (decl', e')++unscopeType :: SrcLoc+            -> M.Map VName Ident+            -> PatternType+            -> TermTypeM (PatternType, [VName])+unscopeType tloc unscoped t = do+  (t', m) <- runStateT (traverseDims onDim t) mempty+  return (t' `addAliases` S.map unAlias, M.elems m)+  where onDim p (NamedDim d)+          | Just loc <- srclocOf <$> M.lookup (qualLeaf d) unscoped =+              if p == PosImmediate || p == PosParam+              then inst loc $ qualLeaf d+              else return AnyDim+        onDim _ d = return d++        inst loc d = do+          prev <- gets $ M.lookup d+          case prev of+            Just d' -> return $ NamedDim $ qualName d'+            Nothing -> do+              d' <- lift $ newDimVar tloc (Rigid $ RigidOutOfScope loc d) "d"+              modify $ M.insert d d'+              return $ NamedDim $ qualName d'++        unAlias (AliasBound v) | v `M.member` unscoped = AliasFree v+        unAlias a = a++-- 'checkApplyExp' is like 'checkExp', but tries to find the "root+-- function", for better error messages.+checkApplyExp :: UncheckedExp -> TermTypeM (Exp, ApplyOp)++checkApplyExp (Apply e1 e2 _ _ loc) = do+  (e1', (fname, i)) <- checkApplyExp e1+  arg <- checkArg e2+  t <- expType e1'+  (t1, rt, argext, exts) <- checkApply loc (fname, i) t arg+  return (Apply e1' (argExp arg) (Info (diet t1, argext)) (Info rt, Info exts) loc,+          (fname, i+1))++checkApplyExp e = do+  e' <- checkExp e+  return (e',+          (case e' of Var qn _ _ -> Just qn+                      _ -> Nothing,+           0))++checkExp :: UncheckedExp -> TermTypeM Exp++checkExp (Literal val loc) =+  return $ Literal val loc++checkExp (StringLit vs loc) =+  return $ StringLit vs loc++checkExp (IntLit val NoInfo loc) = do+  t <- newTypeVar loc "t"+  mustBeOneOf anyNumberType (mkUsage loc "integer literal") t+  return $ IntLit val (Info $ fromStruct t) loc++checkExp (FloatLit val NoInfo loc) = do+  t <- newTypeVar loc "t"+  mustBeOneOf anyFloatType (mkUsage loc "float literal") t+  return $ FloatLit val (Info $ fromStruct t) loc++checkExp (TupLit es loc) =+  TupLit <$> mapM checkExp es <*> pure loc++checkExp (RecordLit fs loc) = do+  fs' <- evalStateT (mapM checkField fs) mempty++  return $ RecordLit fs' loc+  where checkField (RecordFieldExplicit f e rloc) = do+          errIfAlreadySet f rloc+          modify $ M.insert f rloc+          RecordFieldExplicit f <$> lift (checkExp e) <*> pure rloc+        checkField (RecordFieldImplicit name NoInfo rloc) = do+          errIfAlreadySet name rloc+          (QualName _ name', t) <- lift $ lookupVar rloc $ qualName name+          modify $ M.insert name rloc+          return $ RecordFieldImplicit name' (Info t) rloc++        errIfAlreadySet f rloc = do+          maybe_sloc <- gets $ M.lookup f+          case maybe_sloc of+            Just sloc ->+              lift $ typeError rloc mempty $ "Field" <+> pquote (ppr f) <+>+              "previously defined at" <+> text (locStrRel rloc sloc) <> "."+            Nothing -> return ()++checkExp (ArrayLit all_es _ loc) =+  -- Construct the result type and unify all elements with it.  We+  -- only create a type variable for empty arrays; otherwise we use+  -- the type of the first element.  This significantly cuts down on+  -- the number of type variables generated for pathologically large+  -- multidimensional array literals.+  case all_es of+    [] -> do et <- newTypeVar loc "t"+             t <- arrayOfM loc et (ShapeDecl [ConstDim 0]) Unique+             return $ ArrayLit [] (Info t) loc+    e:es -> do+      e' <- checkExp e+      et <- expType e'+      es' <- mapM (unifies "type of first array element" (toStruct et) <=< checkExp) es+      et' <- normTypeFully et+      t <- arrayOfM loc et' (ShapeDecl [ConstDim $ length all_es]) Unique+      return $ ArrayLit (e':es') (Info t) loc++checkExp (Range start maybe_step end _ loc) = do+  start' <- require "use in range expression" anyIntType =<< checkExp start+  start_t <- toStruct <$> expTypeFully start'+  maybe_step' <- case maybe_step of+    Nothing -> return Nothing+    Just step -> do+      let warning = warn loc "First and second element of range are identical, this will produce an empty array."+      case (start, step) of+        (Literal x _, Literal y _) -> when (x == y) warning+        (Var x_name _ _, Var y_name _ _) -> when (x_name == y_name) warning+        _ -> return ()+      Just <$> (unifies "use in range expression" start_t =<< checkExp step)++  let unifyRange e = unifies "use in range expression" start_t =<< checkExp e+  end' <- case end of+    DownToExclusive e -> DownToExclusive <$> unifyRange e+    UpToExclusive e -> UpToExclusive <$> unifyRange e+    ToInclusive e -> ToInclusive <$> unifyRange e++  -- Special case some ranges to give them a known size.+  let dimFromBound = dimFromExp (SourceBound . bareExp)+  (dim, retext) <-+    case (isInt32 start', isInt32 <$> maybe_step', end') of+      (Just 0, Just (Just 1), UpToExclusive end'') ->+        dimFromBound end''+      (Just 0, Nothing, UpToExclusive end'') ->+        dimFromBound end''+      (Just 1, Just (Just 2), ToInclusive end'') ->+        dimFromBound end''+      _ -> do+        d <- newDimVar loc (Rigid RigidRange) "range_dim"+        return (NamedDim $ qualName d, Just d)++  t <- arrayOfM loc start_t (ShapeDecl [dim]) Unique+  let ret = (Info (t `setAliases` mempty), Info $ maybeToList retext)++  return $ Range start' maybe_step' end' ret loc++checkExp (Ascript e decl loc) = do+  (decl', e') <- checkAscript loc decl e id+  return $ Ascript e' decl' loc++checkExp (Coerce e decl _ loc) = do+  -- We instantiate the declared types with all dimensions as nonrigid+  -- fresh type variables, which we then use to unify with the type of+  -- 'e'.  This lets 'e' have whatever sizes it wants, but the overall+  -- type must still match.  Eventually we will throw away those sizes+  -- (they will end up being unified with various sizes in 'e', which+  -- is fine).+  (decl', e') <- checkAscript loc decl e anySizes++  -- Now we instantiate the declared type again, but this time we keep+  -- around the sizes as existentials.  This is the result of the+  -- ascription as a whole.  We use matchDims to obtain the aliasing+  -- of 'e'.+  (decl_t_rigid, ext) <-+    instantiateDimsInReturnType loc Nothing $ unInfo $ expandedType decl'++  t <- expTypeFully e'++  t' <- matchDims (const pure) t $ fromStruct decl_t_rigid++  return $ Coerce e' decl' (Info t', Info ext) loc++checkExp (BinOp (op, oploc) NoInfo (e1,_) (e2,_) NoInfo NoInfo loc) = do+  (op', ftype) <- lookupVar oploc op+  e1_arg <- checkArg e1+  e2_arg <- checkArg e2++  -- Note that the application to the first operand cannot fix any+  -- existential sizes, because it must by necessity be a function.+  (p1_t, rt, p1_ext, _) <- checkApply loc (Just op', 0) ftype e1_arg+  (p2_t, rt', p2_ext, retext) <- checkApply loc (Just op', 1) rt e2_arg++  return $ BinOp (op', oploc) (Info ftype)+    (argExp e1_arg, Info (toStruct p1_t, p1_ext))+    (argExp e2_arg, Info (toStruct p2_t, p2_ext))+    (Info rt') (Info retext) loc++checkExp (Project k e NoInfo loc) = do+  e' <- checkExp e+  t <- expType e'+  kt <- mustHaveField (mkUsage loc $ "projection of field " ++ quote (pretty k)) k t+  return $ Project k e' (Info kt) loc++checkExp (If e1 e2 e3 _ loc) =+  sequentially checkCond $ \e1' _ -> do+  ((e2', e3'), dflow) <- tapOccurences $ checkExp e2 `alternative` checkExp e3++  (brancht, retext) <- unifyBranches loc e2' e3'+  let t' = addAliases brancht (`S.difference` S.map AliasBound (allConsumed dflow))++  zeroOrderType (mkUsage loc "returning value of this type from 'if' expression")+    "type returned from branch" t'++  return $ If e1' e2' e3' (Info t', Info retext) loc++  where checkCond = do+          e1' <- checkExp e1+          let bool = Scalar $ Prim Bool+          e1_t <- toStruct <$> expType e1'+          onFailure (CheckingRequired [bool] e1_t) $+            unify (mkUsage (srclocOf e1') "use as 'if' condition") bool e1_t+          return e1'++checkExp (Parens e loc) =+  Parens <$> checkExp e <*> pure loc++checkExp (QualParens (modname, modnameloc) e loc) = do+  (modname',mod) <- lookupMod loc modname+  case mod of+    ModEnv env -> local (`withEnv` qualifyEnv modname' env) $ do+      e' <- checkExp e+      return $ QualParens (modname', modnameloc) e' loc+    ModFun{} ->+      typeError loc mempty $ "Module" <+> ppr modname <+> " is a parametric module."+  where qualifyEnv modname' env =+          env { envNameMap = M.map (qualify' modname') $ envNameMap env }+        qualify' modname' (QualName qs name) =+          QualName (qualQuals modname' ++ [qualLeaf modname'] ++ qs) name++checkExp (Var qn NoInfo loc) = do+  -- The qualifiers of a variable is divided into two parts: first a+  -- possibly-empty sequence of module qualifiers, followed by a+  -- possible-empty sequence of record field accesses.  We use scope+  -- information to perform the split, by taking qualifiers off the+  -- end until we find a module.++  (qn', t, fields) <- findRootVar (qualQuals qn) (qualLeaf qn)++  foldM checkField (Var qn' (Info t) loc) fields++  where findRootVar qs name =+          (whenFound <$> lookupVar loc (QualName qs name)) `catchError` notFound qs name++        whenFound (qn', t) = (qn', t, [])++        notFound qs name err+          | null qs = throwError err+          | otherwise = do+              (qn', t, fields) <- findRootVar (init qs) (last qs) `catchError`+                                  const (throwError err)+              return (qn', t, fields++[name])++        checkField e k = do+          t <- expType e+          let usage = mkUsage loc $ "projection of field " ++ quote (pretty k)+          kt <- mustHaveField usage k t+          return $ Project k e (Info kt) loc++checkExp (Negate arg loc) = do+  arg' <- require "numeric negation" anyNumberType =<< checkExp arg+  return $ Negate arg' loc++checkExp e@Apply{} = fst <$> checkApplyExp e++checkExp (LetPat pat e body _ loc) =+  sequentially (checkExp e) $ \e' e_occs -> do+    -- Not technically an ascription, but we want the pattern to have+    -- exactly the type of 'e'.+    t <- expType e'+    case anyConsumption e_occs of+      Just c ->+        let msg = "type computed with consumption at " ++ locStr (location c)+        in zeroOrderType (mkUsage loc "consumption in right-hand side of 'let'-binding") msg t+      _ -> return ()++    incLevel $ bindingPattern pat (Ascribed t) $ \pat' -> do+      body' <- checkExp body+      (body_t, retext) <-+        unscopeType loc (patternMap pat') =<< expTypeFully body'++      return $ LetPat pat' e' body' (Info body_t, Info retext) loc++checkExp (LetFun name (tparams, params, maybe_retdecl, NoInfo, e) body NoInfo loc) =+  sequentially (checkBinding (name, maybe_retdecl, tparams, params, e, loc)) $+  \(tparams', params', maybe_retdecl', rettype, _, e') closure -> do++    closure' <- lexicalClosure params' closure++    bindSpaced [(Term, name)] $ do+      name' <- checkName Term name loc++      let arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt+          ftype = foldr (arrow . patternParam) rettype params'+          entry = BoundV Local tparams' $ ftype `setAliases` closure'+          bindF scope = scope { scopeVtable =+                                  M.insert name' entry $ scopeVtable scope+                              , scopeNameMap =+                                  M.insert (Term, name) (qualName name') $+                                  scopeNameMap scope }+      body' <- localScope bindF $ checkExp body++      -- We fake an ident here, but it's OK as it can't be a size+      -- anyway.+      let fake_ident = Ident name' (Info $ fromStruct ftype) noLoc+      (body_t, _) <-+        unscopeType loc (M.singleton name' fake_ident) =<<+        expTypeFully body'++      return $ LetFun name' (tparams', params', maybe_retdecl', Info rettype, e')+        body' (Info body_t) loc++checkExp (LetWith dest src idxes ve body NoInfo loc) =+  sequentially (checkIdent src) $ \src' _ -> do+  (t, _) <- newArrayType (srclocOf src) "src" $ length idxes+  unify (mkUsage loc "type of target array") t $ toStruct $ unInfo $ identType src'++  -- Need the fully normalised type here to get the proper aliasing information.+  src_t <- normTypeFully $ unInfo $ identType src'++  idxes' <- mapM checkDimIndex idxes+  (elemt, _) <- sliceShape (Just (loc, Nonrigid)) idxes' =<< normTypeFully t++  unless (unique src_t) $+    typeError loc mempty $ "Source" <+> pquote (pprName (identName src)) <+>+    "has type" <+> ppr src_t <+> ", which is not unique."+  vtable <- asks $ scopeVtable . termScope+  forM_ (aliases src_t) $ \v ->+    case aliasVar v `M.lookup` vtable of+      Just (BoundV Local _ v_t)+        | not $ unique v_t ->+            typeError loc mempty $ "Source" <+> pquote (pprName (identName src)) <+>+            "aliases" <+> pquote (pprName (aliasVar v)) <+> ", which is not consumable."+      _ -> return ()++  sequentially (unifies "type of target array" (toStruct elemt) =<< checkExp ve) $ \ve' _ -> do+    ve_t <- expTypeFully ve'+    when (AliasBound (identName src') `S.member` aliases ve_t) $+      badLetWithValue loc++    bindingIdent dest (src_t `setAliases` S.empty) $ \dest' -> do+      body' <- consuming src' $ checkExp body+      (body_t, _) <-+        unscopeType loc (M.singleton (identName dest') dest') =<<+        expTypeFully body'+      return $ LetWith dest' src' idxes' ve' body' (Info body_t) loc++checkExp (Update src idxes ve loc) = do+  (t, _) <- newArrayType (srclocOf src) "src" $ length idxes+  idxes' <- mapM checkDimIndex idxes+  (elemt, _) <- sliceShape (Just (loc, Nonrigid)) idxes' =<< normTypeFully t++  sequentially (checkExp ve >>= unifies "type of target array" elemt) $ \ve' _ ->+    sequentially (checkExp src >>= unifies "type of target array" t) $ \src' _ -> do++    src_t <- expTypeFully src'+    unless (unique src_t) $+      typeError loc mempty $ "Source" <+> pquote (ppr src) <+>+      "has type" <+> ppr src_t <+> ", which is not unique."++    let src_als = aliases src_t+    ve_t <- expTypeFully ve'+    unless (S.null $ src_als `S.intersection` aliases ve_t) $ badLetWithValue loc++    consume loc src_als+    return $ Update src' idxes' ve' loc++-- Record updates are a bit hacky, because we do not have row typing+-- (yet?).  For now, we only permit record updates where we know the+-- full type up to the field we are updating.+checkExp (RecordUpdate src fields ve NoInfo loc) = do+  src' <- checkExp src+  ve' <- checkExp ve+  a <- expTypeFully src'+  let usage = mkUsage loc "record update"+  r <- foldM (flip $ mustHaveField usage) a fields+  ve_t <- expType ve'+  let r' = anySizes $ toStruct r+      ve_t' = anySizes $ toStruct ve_t+  onFailure (CheckingRecordUpdate fields r' ve_t') $+    unify usage r' ve_t'+  maybe_a' <- onRecordField (const ve_t) fields <$> expTypeFully src'+  case maybe_a' of+    Just a' -> return $ RecordUpdate src' fields ve' (Info a') loc+    Nothing -> typeError loc mempty $+               "Full type of" </>+               indent 2 (ppr src) </>+               textwrap " is not known at this point.  Add a size annotation to the original record to disambiguate."++checkExp (Index e idxes _ loc) = do+  (t, _) <- newArrayType loc "e" $ length idxes+  e' <- unifies "being indexed at" t =<< checkExp e+  idxes' <- mapM checkDimIndex idxes+  -- XXX, the RigidSlice here will be overridden in sliceShape with a proper value.+  (t', retext) <-+    sliceShape (Just (loc, Rigid (RigidSlice Nothing ""))) idxes' =<<+    expTypeFully e'++  -- Remove aliases if the result is an overloaded type, because that+  -- will certainly not be aliased.+  t'' <- noAliasesIfOverloaded t'++  return $ Index e' idxes' (Info t'', Info retext) loc++checkExp (Unsafe e loc) =+  Unsafe <$> checkExp e <*> pure loc++checkExp (Assert e1 e2 NoInfo loc) = do+  e1' <- require "being asserted" [Bool] =<< checkExp e1+  e2' <- checkExp e2+  return $ Assert e1' e2' (Info (pretty e1)) loc++checkExp (Lambda params body rettype_te NoInfo loc) =+  removeSeminullOccurences $ noUnique $ incLevel $+  bindingParams [] params $ \_ params' -> do+    rettype_checked <- traverse checkTypeExp rettype_te+    let declared_rettype =+          case rettype_checked of Just (_, st, _) -> Just st+                                  Nothing -> Nothing+    (body', closure) <-+      tapOccurences $ checkFunBody params' body declared_rettype loc+    body_t <- expTypeFully body'++    params'' <- mapM updateTypes params'++    (rettype', rettype_st) <-+      case rettype_checked of+        Just (te, st, _) ->+          return (Just te, st)+        Nothing -> do+          ret <- inferReturnSizes params'' $ toStruct $+                 inferReturnUniqueness params'' body_t+          return (Nothing, ret)++    checkGlobalAliases params' body_t loc+    verifyFunctionParams Nothing params'++    closure' <- lexicalClosure params'' closure++    return $ Lambda params'' body' rettype' (Info (closure', rettype_st)) loc++  where+    -- Inferring the sizes of the return type of a lambda is a lot+    -- like let-generalisation.  We wish to remove any rigid sizes+    -- that were created when checking the body, except for those that+    -- are visible in types that existed before we entered the body,+    -- are parameters, or are used in parameters.+    inferReturnSizes params' ret = do+      cur_lvl <- curLevel+      let named (Named x, _) = Just x+          named (Unnamed, _) = Nothing+          param_names = mapMaybe (named . patternParam) params'+          pos_sizes =+            typeDimNamesPos (foldFunType (map patternStructType params') ret)+          hide k (lvl, _) =+            lvl >= cur_lvl && k `notElem` param_names && k `S.notMember` pos_sizes++      hidden_sizes <-+        S.fromList . M.keys . M.filterWithKey hide <$> getConstraints++      let onDim (NamedDim name)+            | not (qualLeaf name `S.member` hidden_sizes) = NamedDim name+            | otherwise = AnyDim+          onDim d = d++      return $ first onDim ret++checkExp (OpSection op _ loc) = do+  (op', ftype) <- lookupVar loc op+  return $ OpSection op' (Info ftype) loc++checkExp (OpSectionLeft op _ e _ _ loc) = do+  (op', ftype) <- lookupVar loc op+  e_arg <- checkArg e+  (t1, rt, argext, retext) <- checkApply loc (Just op', 0) ftype e_arg+  case rt of+    Scalar (Arrow _ _ t2 rettype) ->+      return $ OpSectionLeft op' (Info ftype) (argExp e_arg)+      (Info (toStruct t1, argext), Info $ toStruct t2) (Info rettype, Info retext) loc+    _ -> typeError loc mempty $+         "Operator section with invalid operator of type" <+> ppr ftype++checkExp (OpSectionRight op _ e _ NoInfo loc) = do+  (op', ftype) <- lookupVar loc op+  e_arg <- checkArg e+  case ftype of+    Scalar (Arrow as1 m1 t1 (Scalar (Arrow as2 m2 t2 ret))) -> do+      (t2', _, argext, _) <-+        checkApply loc (Just op', 1)+        (Scalar $ Arrow as2 m2 t2 $ Scalar $ Arrow as1 m1 t1 ret) e_arg+      return $ OpSectionRight op' (Info ftype) (argExp e_arg)+        (Info $ toStruct t1, Info (toStruct t2', argext)) (Info ret) loc+    _ -> typeError loc mempty $+         "Operator section with invalid operator of type" <+> ppr ftype++checkExp (ProjectSection fields NoInfo loc) = do+  a <- newTypeVar loc "a"+  let usage = mkUsage loc "projection at"+  b <- foldM (flip $ mustHaveField usage) a fields+  return $ ProjectSection fields (Info $ Scalar $ Arrow mempty Unnamed a b) loc++checkExp (IndexSection idxes NoInfo loc) = do+  (t, _) <- newArrayType loc "e" $ length idxes+  idxes' <- mapM checkDimIndex idxes+  (t', _) <- sliceShape Nothing idxes' t+  return $ IndexSection idxes' (Info $ fromStruct $ Scalar $ Arrow mempty Unnamed t t') loc++checkExp (DoLoop _ mergepat mergeexp form loopbody NoInfo loc) =+  sequentially (checkExp mergeexp) $ \mergeexp' _ -> do++  zeroOrderType (mkUsage (srclocOf mergeexp) "use as loop variable")+    "type used as loop variable" =<< expTypeFully mergeexp'++  -- The handling of dimension sizes is a bit intricate, but very+  -- similar to checking a function, followed by checking a call to+  -- it.  The overall procedure is as follows:+  --+  -- (1) All empty dimensions in the merge pattern are instantiated+  -- with nonrigid size variables.  All explicitly specified+  -- dimensions are preserved.+  --+  -- (2) The body of the loop is type-checked.  The result type is+  -- combined with the merge pattern type to determine which sizes are+  -- variant, and these are turned into size parameters for the merge+  -- pattern.+  --+  -- (3) We now conceptually have a function parameter type and return+  -- type.  We check that it can be called with the initial merge+  -- values as argument.  The result of this is the type of the loop+  -- as a whole.+  --+  -- (There is also a convergence loop for inferring uniqueness, but+  -- that's orthogonal to the size handling.)++  (merge_t, new_dims) <-+    instantiateEmptyArrayDims loc "loop" Nonrigid . -- dim handling (1)+    anySizes .+    (`setAliases` mempty) =<< expTypeFully mergeexp'++  -- dim handling (2)+  let checkLoopReturnSize mergepat' loopbody' = do+        loopbody_t <- expTypeFully loopbody'+        pat_t <- normTypeFully $ patternType mergepat'+        -- We are ignoring the dimensions here, because any mismatches+        -- should be turned into fresh size variables.+        onFailure (CheckingLoopBody (toStruct (anySizes pat_t)) (toStruct loopbody_t)) $+          expect (mkUsage (srclocOf loopbody) "matching loop body to loop pattern")+          (toStruct (anySizes pat_t))+          (toStruct loopbody_t)+        pat_t' <- normTypeFully pat_t+        loopbody_t' <- normTypeFully loopbody_t++        -- For each new_dims, figure out what they are instantiated+        -- with in the initial value.  This is used to determine+        -- whether a size is invariant because it always matches the+        -- initial instantiation of that size.+        let initSubst (NamedDim v, d) = Just (v, d)+            initSubst _ = Nothing+        init_substs <- M.fromList . mapMaybe initSubst . snd .+                       anyDimOnMismatch pat_t' <$>+                       expTypeFully mergeexp'++        -- Figure out which of the 'new_dims' dimensions are variant.+        -- This works because we know that each dimension from+        -- new_dims in the pattern is unique and distinct.+        --+        -- Our logic here is a bit reversed: the *mismatches* (from+        -- new_dims) are what we want to extract and turn into size+        -- parameters.+        let mismatchSubst (NamedDim v, d)+              | qualLeaf v `elem` new_dims =+                  case M.lookup v init_substs of+                    Just d'+                      | d' == d ->+                          return $ Just (qualLeaf v, SizeSubst d)+                    _ -> do tell [qualLeaf v]+                            return Nothing+            mismatchSubst _ = return Nothing++            (init_substs', sparams) =+              runWriter $ M.fromList . catMaybes <$> mapM mismatchSubst+              (snd $ anyDimOnMismatch pat_t' loopbody_t')++        -- Make sure that any of new_dims that are invariant will be+        -- replaced with the invariant size in the loop body.  Failure+        -- to do this can cause type annotations to still refer to+        -- new_dims.+        let dimToInit (v, SizeSubst d) =+              constrain v $ Size (Just d) (mkUsage loc "size of loop parameter")+            dimToInit _ =+              return ()+        mapM_ dimToInit $ M.toList init_substs'++        mergepat'' <- applySubst (`M.lookup` init_substs') <$> updateTypes mergepat'+        return (nub sparams, mergepat'')++  -- First we do a basic check of the loop body to figure out which of+  -- the merge parameters are being consumed.  For this, we first need+  -- to check the merge pattern, which requires the (initial) merge+  -- expression.+  --+  -- Play a little with occurences to ensure it does not look like+  -- none of the merge variables are being used.+  ((sparams, mergepat', form', loopbody'), bodyflow) <-+    case form of+      For i uboundexp -> do+        uboundexp' <- require "being the bound in a 'for' loop" anySignedType =<< checkExp uboundexp+        bound_t <- expTypeFully uboundexp'+        bindingIdent i bound_t $ \i' ->+          noUnique $ bindingPattern mergepat (Ascribed merge_t) $+          \mergepat' -> onlySelfAliasing $ tapOccurences $ do+            loopbody' <- noSizeEscape $ checkExp loopbody+            (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+            return (sparams,+                    mergepat'',+                    For i' uboundexp',+                    loopbody')++      ForIn xpat e -> do+        (arr_t, _) <- newArrayType (srclocOf e) "e" 1+        e' <- unifies "being iterated in a 'for-in' loop" arr_t =<< checkExp e+        t <- expTypeFully e'+        case t of+          _ | Just t' <- peelArray 1 t ->+                bindingPattern xpat (Ascribed t') $ \xpat' ->+                noUnique $ bindingPattern mergepat (Ascribed merge_t) $+                \mergepat' -> onlySelfAliasing $ tapOccurences $ do+                  loopbody' <- noSizeEscape $ checkExp loopbody+                  (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+                  return (sparams,+                          mergepat'',+                          ForIn xpat' e',+                          loopbody')+            | otherwise ->+                typeError (srclocOf e) mempty $+                "Iteratee of a for-in loop must be an array, but expression has type" <+>+                ppr t++      While cond ->+        noUnique $ bindingPattern mergepat (Ascribed merge_t) $ \mergepat' ->+        onlySelfAliasing $ tapOccurences $+        sequentially (checkExp cond >>=+                      unifies "being the condition of a 'while' loop" (Scalar $ Prim Bool)) $ \cond' _ -> do+          loopbody' <- noSizeEscape $ checkExp loopbody+          (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+          return (sparams,+                  mergepat'',+                  While cond',+                  loopbody')++  mergepat'' <- do+    loop_t <- expTypeFully loopbody'+    convergePattern mergepat' (allConsumed bodyflow) loop_t $+      mkUsage (srclocOf loopbody') "being (part of) the result of the loop body"++  let consumeMerge (Id _ (Info pt) ploc) mt+        | unique pt = consume ploc $ aliases mt+      consumeMerge (TuplePattern pats _) t | Just ts <- isTupleRecord t =+        zipWithM_ consumeMerge pats ts+      consumeMerge (PatternParens pat _) t =+        consumeMerge pat t+      consumeMerge (PatternAscription pat _ _) t =+        consumeMerge pat t+      consumeMerge _ _ =+        return ()+  consumeMerge mergepat'' =<< expTypeFully mergeexp'++  -- dim handling (3)+  let sparams_anydim = M.fromList $ zip sparams $ repeat $ SizeSubst AnyDim+      loopt_anydims = applySubst (`M.lookup` sparams_anydim) $+                      patternType mergepat''+  (merge_t', _) <-+    instantiateEmptyArrayDims loc "loopres" Nonrigid $ toStruct loopt_anydims+  mergeexp_t <- toStruct <$> expTypeFully mergeexp'+  onFailure (CheckingLoopInitial (toStruct loopt_anydims) mergeexp_t) $+    unify (mkUsage (srclocOf mergeexp') "matching initial loop values to pattern")+    merge_t' mergeexp_t++  (loopt, retext) <- instantiateDimsInType loc RigidLoop loopt_anydims+  -- We set all of the uniqueness to be unique.  This is intentional,+  -- and matches what happens for function calls.  Those arrays that+  -- really *cannot* be consumed will alias something unconsumable,+  -- and will be caught that way.+  let bound_here = patternNames mergepat'' <> S.fromList sparams <> form_bound+      form_bound =+        case form' of+          For v _ -> S.singleton $ identName v+          ForIn forpat _ -> patternNames forpat+          While{} -> mempty+      loopt' = second (`S.difference` S.map AliasBound bound_here) $+               loopt `setUniqueness` Unique+++  -- Eliminate those new_dims that turned into sparams so it won't+  -- look like we have ambiguous sizes lying around.+  modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` sparams++  return $ DoLoop sparams mergepat'' mergeexp' form' loopbody' (Info (loopt', retext)) loc++  where+    convergePattern pat body_cons body_t body_loc = do+      let consumed_merge = patternNames pat `S.intersection` body_cons++          uniquePat (Wildcard (Info t) wloc) =+            Wildcard (Info $ t `setUniqueness` Nonunique) wloc+          uniquePat (PatternParens p ploc) =+            PatternParens (uniquePat p) ploc+          uniquePat (Id name (Info t) iloc)+            | name `S.member` consumed_merge =+                let t' = t `setUniqueness` Unique `setAliases` mempty+                in Id name (Info t') iloc+            | otherwise =+                let t' = case t of Scalar Record{} -> t+                                   _               -> t `setUniqueness` Nonunique+                in Id name (Info t') iloc+          uniquePat (TuplePattern pats ploc) =+            TuplePattern (map uniquePat pats) ploc+          uniquePat (RecordPattern fs ploc) =+            RecordPattern (map (fmap uniquePat) fs) ploc+          uniquePat (PatternAscription p t ploc) =+            PatternAscription p t ploc+          uniquePat p@PatternLit{} = p+          uniquePat (PatternConstr n t ps ploc) =+            PatternConstr n t (map uniquePat ps) ploc++          -- Make the pattern unique where needed.+          pat' = uniquePat pat++      pat_t <- normTypeFully $ patternType pat'+      unless (toStructural body_t `subtypeOf` toStructural pat_t) $+        unexpectedType (srclocOf body_loc) (toStruct body_t) [toStruct pat_t]++      -- Check that the new values of consumed merge parameters do not+      -- alias something bound outside the loop, AND that anything+      -- returned for a unique merge parameter does not alias anything+      -- else returned.  We also update the aliases for the pattern.+      bound_outside <- asks $ S.fromList . M.keys . scopeVtable . termScope+      let combAliases t1 t2 =+            case t1 of Scalar Record{} -> t1+                       _ -> t1 `addAliases` (<>aliases t2)++          checkMergeReturn (Id pat_v (Info pat_v_t) patloc) t+            | unique pat_v_t,+              v:_ <- S.toList $+                     S.map aliasVar (aliases t) `S.intersection` bound_outside =+                lift $ typeError loc mempty $+                "Return value for loop parameter" <+>+                pquote (pprName pat_v) <+> "aliases" <+> pprName v <> "."++            | otherwise = do+                (cons,obs) <- get+                unless (S.null $ aliases t `S.intersection` cons) $+                  lift $ typeError loc mempty $+                  "Return value for loop parameter" <+>+                  pquote (pprName pat_v) <+>+                  "aliases other consumed loop parameter."+                when (unique pat_v_t &&+                      not (S.null (aliases t `S.intersection` (cons<>obs)))) $+                  lift $ typeError loc mempty $+                  "Return value for consuming loop parameter" <+>+                  pquote (pprName pat_v) <+> "aliases previously returned value."+                if unique pat_v_t+                  then put (cons<>aliases t, obs)+                  else put (cons, obs<>aliases t)++                return $ Id pat_v (Info (combAliases pat_v_t t)) patloc++          checkMergeReturn (Wildcard (Info pat_v_t) patloc) t =+            return $ Wildcard (Info (combAliases pat_v_t t)) patloc++          checkMergeReturn (PatternParens p _) t =+            checkMergeReturn p t++          checkMergeReturn (PatternAscription p _ _) t =+            checkMergeReturn p t++          checkMergeReturn (RecordPattern pfs patloc) (Scalar (Record tfs)) =+            RecordPattern . M.toList <$> sequence pfs' <*> pure patloc+            where pfs' = M.intersectionWith checkMergeReturn+                         (M.fromList pfs) tfs++          checkMergeReturn (TuplePattern pats patloc) t+            | Just ts <- isTupleRecord t =+                TuplePattern+                <$> zipWithM checkMergeReturn pats ts+                <*> pure patloc++          checkMergeReturn p _ =+            return p++      (pat'', (pat_cons, _)) <-+        runStateT (checkMergeReturn pat' body_t) (mempty, mempty)++      let body_cons' = body_cons <> S.map aliasVar pat_cons+      if body_cons' == body_cons && patternType pat'' == patternType pat+        then return pat'+        else convergePattern pat'' body_cons' body_t body_loc++checkExp (Constr name es NoInfo loc) = do+  t <- newTypeVar loc "t"+  es' <- mapM checkExp es+  ets <- mapM expTypeFully es'+  mustHaveConstr (mkUsage loc "use of constructor") name t (toStruct <$> ets)+  -- A sum value aliases *anything* that went into its construction.+  let als = foldMap aliases ets+  return $ Constr name es' (Info $ fromStruct t `addAliases` (<>als)) loc++checkExp (Match e cs _ loc) =+  sequentially (checkExp e) $ \e' _ -> do+    mt <- expTypeFully e'+    (cs', t, retext) <- checkCases mt cs+    zeroOrderType (mkUsage loc "being returned 'match'")+      "type returned from pattern match" t+    return $ Match e' cs' (Info t, Info retext) loc++checkCases :: PatternType+           -> NE.NonEmpty (CaseBase NoInfo Name)+           -> TermTypeM (NE.NonEmpty (CaseBase Info VName), PatternType, [VName])+checkCases mt rest_cs =+  case NE.uncons rest_cs of+    (c, Nothing) -> do+      (c', t, retext) <- checkCase mt c+      return (c' NE.:| [], t, retext)+    (c, Just cs) -> do+      (((c', c_t, _), (cs', cs_t, _)), dflow) <-+        tapOccurences $ checkCase mt c `alternative` checkCases mt cs+      (brancht, retext) <- unifyBranchTypes (srclocOf c) c_t cs_t+      let t = addAliases brancht+              (`S.difference` S.map AliasBound (allConsumed dflow))+      return (NE.cons c' cs', t, retext)++checkCase :: PatternType -> CaseBase NoInfo Name+          -> TermTypeM (CaseBase Info VName, PatternType, [VName])+checkCase mt (CasePat p e loc) =+  bindingPattern p (Ascribed mt) $ \p' -> do+    e' <- checkExp e+    (t, retext) <- unscopeType loc (patternMap p') =<< expTypeFully e'+    return (CasePat p' e' loc, t, retext)++-- | An unmatched pattern. Used in in the generation of+-- unmatched pattern warnings by the type checker.+data Unmatched p = UnmatchedNum p [ExpBase Info VName]+                 | UnmatchedBool p+                 | UnmatchedConstr p+                 | Unmatched p+                 deriving (Functor, Show)++instance Pretty (Unmatched (PatternBase Info VName)) where+  ppr um = case um of+      (UnmatchedNum p nums) -> ppr' p <+> "where p is not one of" <+> ppr nums+      (UnmatchedBool p)     -> ppr' p+      (UnmatchedConstr p)     -> ppr' p+      (Unmatched p)         -> ppr' p+    where+      ppr' (PatternAscription p t _) = ppr p <> ":" <+> ppr t+      ppr' (PatternParens p _)       = parens $ ppr' p+      ppr' (Id v _ _)                = pprName v+      ppr' (TuplePattern pats _)     = parens $ commasep $ map ppr' pats+      ppr' (RecordPattern fs _)      = braces $ commasep $ map ppField fs+        where ppField (name, t)      = text (nameToString name) <> equals <> ppr' t+      ppr' Wildcard{}                = "_"+      ppr' (PatternLit e _ _)        = ppr e+      ppr' (PatternConstr n _ ps _)   = "#" <> ppr n <+> sep (map ppr' ps)++unpackPat :: Pattern -> [Maybe Pattern]+unpackPat Wildcard{} = [Nothing]+unpackPat (PatternParens p _) = unpackPat p+unpackPat Id{} = [Nothing]+unpackPat (TuplePattern ps _) = Just <$> ps+unpackPat (RecordPattern fs _) = Just . snd <$> sortFields (M.fromList fs)+unpackPat (PatternAscription p _ _) = unpackPat p+unpackPat p@PatternLit{} = [Just p]+unpackPat p@PatternConstr{} = [Just p]++wildPattern :: Pattern -> Int -> Unmatched Pattern -> Unmatched Pattern+wildPattern (TuplePattern ps loc) pos um = wildTuple <$> um+  where wildTuple p = TuplePattern (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc+        ps' = map wildOut ps+        wildOut p = Wildcard (Info (patternType p)) (srclocOf p)+wildPattern (RecordPattern fs loc) pos um = wildRecord <$> um+  where wildRecord p =+          RecordPattern (take (pos - 1) fs' ++ [(fst (fs!!(pos - 1)), p)] ++ drop pos fs') loc+        fs' = map wildOut fs+        wildOut (f,p) = (f, Wildcard (Info (patternType p)) (srclocOf p))+wildPattern (PatternAscription p _ _) pos um = wildPattern p pos um+wildPattern (PatternParens p _) pos um = wildPattern p pos um+wildPattern (PatternConstr n t ps loc) pos um = wildConstr <$> um+  where wildConstr p = PatternConstr n t (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc+        ps' = map wildOut ps+        wildOut p = Wildcard (Info (patternType p)) (srclocOf p)+wildPattern _ _ um = um++checkUnmatched :: Exp -> TermTypeM ()+checkUnmatched e = void $ checkUnmatched' e >> astMap tv e+  where checkUnmatched' (Match _ cs _ loc) =+          let ps = fmap (\(CasePat p _ _) -> p) cs+          in case unmatched id $ NE.toList ps of+              []  -> return ()+              ps' -> typeError loc mempty $+                     "Unmatched cases in match expression:" </>+                     indent 2 (stack (map ppr ps'))+        checkUnmatched' _ = return ()+        tv = ASTMapper { mapOnExp =+                           \e' -> checkUnmatched' e' >> return e'+                       , mapOnName        = pure+                       , mapOnQualName    = pure+                       , mapOnStructType  = pure+                       , mapOnPatternType = pure+                       }++-- | A data type for constructor patterns.  This is used to make the+-- code for detecting unmatched constructors cleaner, by separating+-- the constructor-pattern cases from other cases.+data ConstrPat = ConstrPat { constrName :: Name+                           , constrType :: PatternType+                           , constrPayload :: [Pattern]+                           , constrSrcLoc :: SrcLoc+                           }++-- Be aware of these fishy equality instances!++instance Eq ConstrPat where+  ConstrPat c1 _ _ _ == ConstrPat c2 _ _ _ = c1 == c2++instance Ord ConstrPat where+  ConstrPat c1 _ _ _ `compare` ConstrPat c2 _ _ _ = c1 `compare` c2++unmatched :: (Unmatched Pattern -> Unmatched Pattern) -> [Pattern] -> [Unmatched Pattern]+unmatched hole orig_ps+  | p:_ <- orig_ps,+    sameStructure labeledCols = do+    (i, cols) <- labeledCols+    let hole' = if isConstr p then hole else hole . wildPattern p i+    case sequence cols of+      Nothing -> []+      Just cs+        | all isPatternLit cs  -> map hole' $ localUnmatched cs+        | otherwise            -> unmatched hole' cs+  | otherwise = []++  where labeledCols = zip [1..] $ transpose $ map unpackPat orig_ps++        localUnmatched :: [Pattern] -> [Unmatched Pattern]+        localUnmatched [] = []+        localUnmatched ps'@(p':_) =+          case patternType p'  of+            Scalar (Sum cs'') ->+              -- We now know that we are matching a sum type, and thus+              -- that all patterns ps' are constructors (checked by+              -- 'all isPatternLit' before this function is called).+              let constrs   = M.keys cs''+                  matched   = mapMaybe constr ps'+                  unmatched' = map (UnmatchedConstr . buildConstr cs'') $+                               constrs \\ map constrName matched+             in case unmatched' of+                [] ->+                  let constrGroups   = group (sort matched)+                      removedConstrs = mapMaybe stripConstrs constrGroups+                      transposed     = (fmap . fmap) transpose removedConstrs+                      findUnmatched (pc, trans) = do+                        col <- trans+                        case col of+                          []           -> []+                          ((i, _):_) -> unmatched (wilder i pc) (map snd col)+                      wilder i pc s = (`PatternParens` noLoc) <$> wildPattern pc i s+                  in concatMap findUnmatched transposed+                _ -> unmatched'+            Scalar (Prim t) | not (any idOrWild ps') ->+              -- We now know that we are matching a sum type, and thus+              -- that all patterns ps' are literals (checked by 'all+              -- isPatternLit' before this function is called).+                case t of+                  Bool ->+                    let matched = nub $ mapMaybe (pExp >=> bool) $ filter isPatternLit ps'+                    in map (UnmatchedBool . buildBool (Scalar (Prim t))) $ [True, False] \\ matched+                  _ ->+                    let matched = mapMaybe pExp $ filter isPatternLit ps'+                    in [UnmatchedNum (buildId (Info $ Scalar $ Prim t) "p") matched]+            _ -> []++        isConstr PatternConstr{} = True+        isConstr (PatternParens p _) = isConstr p+        isConstr _ = False+++        stripConstrs :: [ConstrPat] -> Maybe (Pattern, [[(Int, Pattern)]])+        stripConstrs (pc@ConstrPat{} : cs') = Just (unConstr pc, stripConstr pc : map stripConstr cs')+        stripConstrs [] = Nothing++        stripConstr :: ConstrPat -> [(Int, Pattern)]+        stripConstr (ConstrPat _ _  ps' _) = zip [1..] ps'++        sameStructure [] = True+        sameStructure (x:xs) = all (\y -> length y == length x' ) xs'+          where (x':xs') = map snd (x:xs)++        pExp (PatternLit e' _ _) = Just e'+        pExp _ = Nothing++        constr (PatternConstr c (Info t) ps loc) = Just $ ConstrPat c t ps loc+        constr (PatternParens p _) = constr p+        constr (PatternAscription p' _ _)  = constr p'+        constr _ = Nothing++        unConstr p =+          PatternConstr (constrName p) (Info $ constrType p) (constrPayload p) (constrSrcLoc p)++        isPatternLit PatternLit{} = True+        isPatternLit (PatternAscription p' _ _) = isPatternLit p'+        isPatternLit (PatternParens p' _)  = isPatternLit p'+        isPatternLit PatternConstr{} = True+        isPatternLit _ = False++        idOrWild Id{} = True+        idOrWild Wildcard{} = True+        idOrWild (PatternAscription p' _ _) = idOrWild p'+        idOrWild (PatternParens p' _) = idOrWild p'+        idOrWild _ = False++        bool (Literal (BoolValue b) _ ) = Just b+        bool _ = Nothing++        buildConstr m c =+          let t      = Scalar $ Sum m+              cs     = m M.! c+              wildCS = map (\ct -> Wildcard (Info ct) noLoc) cs+          in if null wildCS+               then PatternConstr c (Info t) [] noLoc+               else PatternParens (PatternConstr c (Info t) wildCS noLoc) noLoc+        buildBool t b =+          PatternLit (Literal (BoolValue b) noLoc) (Info (addSizes t)) noLoc+        buildId t n =+          -- The VName tag here will never be used since the value+          -- exists exclusively for printing warnings.+          Id (VName (nameFromString n) (-1)) t noLoc++checkIdent :: IdentBase NoInfo Name -> TermTypeM Ident+checkIdent (Ident name _ loc) = do+  (QualName _ name', vt) <- lookupVar loc (qualName name)+  return $ Ident name' (Info vt) loc++checkDimIndex :: DimIndexBase NoInfo Name -> TermTypeM DimIndex+checkDimIndex (DimFix i) =+  DimFix <$> (unifies "use as index" (Scalar $ Prim $ Signed Int32) =<< checkExp i)+checkDimIndex (DimSlice i j s) =+  DimSlice <$> check i <*> check j <*> check s+  where check = maybe (return Nothing) $+                fmap Just . unifies "use as index" (Scalar $ Prim $ Signed Int32) <=< checkExp++sequentially :: TermTypeM a -> (a -> Occurences -> TermTypeM b) -> TermTypeM b+sequentially m1 m2 = do+  (a, m1flow) <- collectOccurences m1+  (b, m2flow) <- collectOccurences $ m2 a m1flow+  occur $ m1flow `seqOccurences` m2flow+  return b++type Arg = (Exp, PatternType, Occurences, SrcLoc)++argExp :: Arg -> Exp+argExp (e, _, _, _) = e++argType :: Arg -> PatternType+argType (_, t, _, _) = t++checkArg :: UncheckedExp -> TermTypeM Arg+checkArg arg = do+  (arg', dflow) <- collectOccurences $ checkExp arg+  arg_t <- expType arg'+  return (arg', arg_t, dflow, srclocOf arg')++instantiateDimsInType :: SrcLoc -> RigidSource+                      -> TypeBase (DimDecl VName) als+                      -> TermTypeM (TypeBase (DimDecl VName) als, [VName])+instantiateDimsInType tloc rsrc =+  instantiateEmptyArrayDims tloc "d" $ Rigid rsrc++instantiateDimsInReturnType :: SrcLoc -> Maybe (QualName VName)+                            -> TypeBase (DimDecl VName) als+                            -> TermTypeM (TypeBase (DimDecl VName) als, [VName])+instantiateDimsInReturnType tloc fname =+  instantiateEmptyArrayDims tloc "ret" $ Rigid $ RigidRet fname++-- Some information about the function/operator we are trying to+-- apply, and how many arguments it has previously accepted.  Used for+-- generating nicer type errors.+type ApplyOp = (Maybe (QualName VName), Int)++checkApply :: SrcLoc -> ApplyOp -> PatternType -> Arg+           -> TermTypeM (PatternType, PatternType, Maybe VName, [VName])+checkApply loc (fname, _)+           (Scalar (Arrow as pname tp1 tp2))+           (argexp, argtype, dflow, argloc) =+  onFailure (CheckingApply fname argexp (toStruct tp1) (toStruct argtype)) $ do+  expect (mkUsage argloc "use as function argument") (toStruct tp1) (toStruct argtype)++  -- Perform substitutions of instantiated variables in the types.+  tp1' <- normTypeFully tp1+  (tp2', ext) <- instantiateDimsInReturnType loc fname =<< normTypeFully tp2+  argtype' <- normTypeFully argtype++  -- Check whether this would produce an impossible return type.+  let (_, tp2_paramdims, _) = dimUses $ toStruct tp2'+  case filter (`S.member` tp2_paramdims) ext of+    [] -> return ()+    ext_paramdims -> do+      let onDim (NamedDim qn)+            | qualLeaf qn `elem` ext_paramdims = AnyDim+          onDim d = d+      typeError loc mempty $+        "Anonymous size would appear in function parameter of return type:" </>+        indent 2 (ppr (first onDim tp2')) </>+        textwrap "This is usually because a higher-order function is used with functional arguments that return anonymous sizes, which are then used as parameters of other function arguments."++  occur [observation as loc]++  checkOccurences dflow+  occurs <- consumeArg argloc argtype' (diet tp1')++  case anyConsumption dflow of+    Just c ->+      let msg = "type of expression with consumption at " ++ locStr (location c)+      in zeroOrderType (mkUsage argloc "potential consumption in expression") msg tp1+    _ -> return ()++  occur $ dflow `seqOccurences` occurs+  (argext, parsubst) <-+    case pname of+      Named pname' -> do+        (d, argext) <- sizeSubst tp1' argexp+        return (argext,+                (`M.lookup` M.singleton pname' (SizeSubst d)))+      _ -> return (Nothing, const Nothing)+  let tp2'' = applySubst parsubst $ returnType tp2' (diet tp1') argtype'++  return (tp1', tp2'', argext, ext)+  where sizeSubst (Scalar (Prim (Signed Int32))) e = dimFromArg fname e+        sizeSubst _ _ = return (AnyDim, Nothing)++checkApply loc fname tfun@(Scalar TypeVar{}) arg = do+  tv <- newTypeVar loc "b"+  unify (mkUsage loc "use as function") (toStruct tfun) $+    Scalar $ Arrow mempty Unnamed (toStruct (argType arg)) tv+  tfun' <- normPatternType tfun+  checkApply loc fname tfun' arg++checkApply loc (fname, prev_applied) ftype (argexp, _, _, _) = do+  let fname' = maybe "expression" (pquote . ppr) fname++  typeError loc mempty $+    if prev_applied == 0+    then "Cannot apply" <+> fname' <+> "as function, as it has type:" </>+         indent 2 (ppr ftype)+    else "Cannot apply" <+> fname' <+> "to argument #" <> ppr (prev_applied+1) <+>+         pquote (shorten $ pretty $ flatten $ ppr argexp) <> "," <+/>+         "as" <+> fname' <+> "only takes" <+> ppr prev_applied <+>+         arguments <> "."+  where arguments | prev_applied == 1 = "argument"+                  | otherwise = "arguments"++isInt32 :: Exp -> Maybe Int32+isInt32 (Literal (SignedValue (Int32Value k')) _) = Just $ fromIntegral k'+isInt32 (IntLit k' _ _) = Just $ fromInteger k'+isInt32 (Negate x _) = negate <$> isInt32 x+isInt32 _ = Nothing++maybeDimFromExp :: Exp -> Maybe (DimDecl VName)+maybeDimFromExp (Var v _ _) = Just $ NamedDim v+maybeDimFromExp (Parens e _) = maybeDimFromExp e+maybeDimFromExp (QualParens _ e _) = maybeDimFromExp e+maybeDimFromExp e = ConstDim . fromIntegral <$> isInt32 e++dimFromExp :: (Exp -> SizeSource) -> Exp -> TermTypeM (DimDecl VName, Maybe VName)+dimFromExp rf (Parens e _) = dimFromExp rf e+dimFromExp rf (QualParens _ e _) = dimFromExp rf e+dimFromExp rf e+  | Just d <- maybeDimFromExp e =+      return (d, Nothing)+  | otherwise =+      extSize (srclocOf e) $ rf e++dimFromArg :: Maybe (QualName VName) -> Exp -> TermTypeM (DimDecl VName, Maybe VName)+dimFromArg fname = dimFromExp $ SourceArg (FName fname) . bareExp++-- | @returnType ret_type arg_diet arg_type@ gives result of applying+-- an argument the given types to a function with the given return+-- type, consuming the argument with the given diet.+returnType :: PatternType+           -> Diet+           -> PatternType+           -> PatternType+returnType (Array _ Unique et shape) _ _ =+  Array mempty Unique et shape+returnType (Array als Nonunique et shape) d arg =+  Array (als<>arg_als) Unique et shape -- Intentional!+  where arg_als = aliases $ maskAliases arg d+returnType (Scalar (Record fs)) d arg =+  Scalar $ Record $ fmap (\et -> returnType et d arg) fs+returnType (Scalar (Prim t)) _ _ =+  Scalar $ Prim t+returnType (Scalar (TypeVar _ Unique t targs)) _ _ =+  Scalar $ TypeVar mempty Unique t targs+returnType (Scalar (TypeVar als Nonunique t targs)) d arg =+  Scalar $ TypeVar (als<>arg_als) Unique t targs -- Intentional!+  where arg_als = aliases $ maskAliases arg d+returnType (Scalar (Arrow _ v t1 t2)) d arg =+  Scalar $ Arrow als v (t1 `setAliases` mempty) (t2 `setAliases` als)+  where als = aliases $ maskAliases arg d+returnType (Scalar (Sum cs)) d arg =+  Scalar $ Sum $ (fmap . fmap) (\et -> returnType et d arg) cs++-- | @t `maskAliases` d@ removes aliases (sets them to 'mempty') from+-- the parts of @t@ that are denoted as consumed by the 'Diet' @d@.+maskAliases :: Monoid as =>+               TypeBase shape as+            -> Diet+            -> TypeBase shape as+maskAliases t Consume = t `setAliases` mempty+maskAliases t Observe = t+maskAliases (Scalar (Record ets)) (RecordDiet ds) =+  Scalar $ Record $ M.intersectionWith maskAliases ets ds+maskAliases t FuncDiet{} = t+maskAliases _ _ = error "Invalid arguments passed to maskAliases."++consumeArg :: SrcLoc -> PatternType -> Diet -> TermTypeM [Occurence]+consumeArg loc (Scalar (Record ets)) (RecordDiet ds) =+  concat . M.elems <$> traverse (uncurry $ consumeArg loc) (M.intersectionWith (,) ets ds)+consumeArg loc (Array _ Nonunique _ _) Consume =+  typeError loc mempty "Consuming parameter passed non-unique argument."+consumeArg loc (Scalar (TypeVar _ Nonunique _ _)) Consume =+  typeError loc mempty "Consuming parameter passed non-unique argument."+consumeArg loc (Scalar (Arrow _ _ t1 _)) (FuncDiet d _)+  | not $ contravariantArg t1 d =+      typeError loc mempty "Non-consuming higher-order parameter passed consuming argument."+  where contravariantArg (Array _ Unique _ _) Observe =+          False+        contravariantArg (Scalar (TypeVar _ Unique _ _)) Observe =+          False+        contravariantArg (Scalar (Record ets)) (RecordDiet ds) =+          and (M.intersectionWith contravariantArg ets ds)+        contravariantArg (Scalar (Arrow _ _ tp tr)) (FuncDiet dp dr) =+          contravariantArg tp dp && contravariantArg tr dr+        contravariantArg _ _ =+          True+consumeArg loc (Scalar (Arrow _ _ _ t2)) (FuncDiet _ pd) =+  consumeArg loc t2 pd+consumeArg loc at Consume = return [consumption (aliases at) loc]+consumeArg loc at _       = return [observation (aliases at) loc]++checkOneExp :: UncheckedExp -> TypeM ([TypeParam], Exp)+checkOneExp e = fmap fst . runTermTypeM $ do+  e' <- checkExp e+  let t = toStruct $ typeOf e'+  (tparams, _, _, _) <-+    letGeneralise (nameFromString "<exp>") (srclocOf e) [] [] t+  e'' <- updateTypes e'+  fixOverloadedTypes+  checkUnmatched e''+  causalityCheck e''+  return (tparams, e'')++-- Verify that all sum type constructors and empty array literals have+-- a size that is known (rigid or a type parameter).  This is to+-- ensure that we can actually determine their shape at run-time.+causalityCheck :: Exp -> TermTypeM ()+causalityCheck binding_body = do+  constraints <- getConstraints++  let checkCausality what known t loc+        | (d,dloc):_ <- mapMaybe (unknown constraints known) $+                        S.toList $ mustBeExplicit $ toStruct t =+            Just $ lift $ causality what loc d dloc t+        | otherwise = Nothing++      checkParamCausality known p =+        checkCausality (ppr p) known (patternType p) (srclocOf p)++      onExp :: S.Set VName -> Exp+            -> StateT (S.Set VName) (Either TypeError) Exp++      onExp known (Var v (Info t) loc)+        | Just bad <- checkCausality (pquote (ppr v)) known t loc =+            bad++      onExp known (ArrayLit [] (Info t) loc)+        | Just bad <- checkCausality "empty array" known t loc =+            bad++      onExp known (Lambda params _ _ _ _)+        | bad : _ <- mapMaybe (checkParamCausality known) params =+            bad++      onExp known e@(LetPat _ bindee_e body_e (_, Info ext) _) = do+        sequencePoint known bindee_e body_e ext+        return e++      onExp known e@(Apply f arg (Info (_, p)) (_, Info ext) _) = do+        sequencePoint known arg f $ maybeToList p ++ ext+        return e++      onExp known e = do+        recurse known e++        case e of+          BinOp _ _ (_, Info (_, xp)) (_, Info (_, yp)) _ (Info ext) _ ->+            modify (<>S.fromList (catMaybes [xp,yp]++ext))+          DoLoop _ _ _ _ _ (Info (_, ext)) _ ->+            modify (<>S.fromList ext)+          If _ _ _ (_, Info ext) _ ->+            modify (<>S.fromList ext)+          Index _ _ (_, Info ext) _ ->+            modify (<>S.fromList ext)+          Match _ _ (_, Info ext) _ ->+            modify (<>S.fromList ext)+          Range _ _ _ (_, Info ext) _ ->+            modify (<>S.fromList ext)+          _ ->+            return ()++        return e++      recurse known = void . astMap mapper+        where mapper = identityMapper { mapOnExp = onExp known }++      sequencePoint known x y ext = do+        new_known <- lift $ execStateT (onExp known x) mempty+        void $ onExp (new_known<>known) y+        modify ((new_known<>S.fromList ext)<>)++  either throwError (const $ return ()) $+    evalStateT (onExp mempty binding_body) mempty+  where unknown constraints known v = do+          guard $ v `S.notMember` known+          loc <- unknowable constraints v+          return (v,loc)++        unknowable constraints v =+          case snd <$> M.lookup v constraints of+            Just (UnknowableSize loc _) -> Just loc+            _                           -> Nothing++        causality what loc d dloc t =+          Left $ TypeError loc mempty $+          "Causality check: size" <+/> pquote (pprName d) <+/>+          "needed for type of" <+> what <> colon </>+          indent 2 (ppr t) </>+          "But" <+> pquote (pprName d) <+> "is computed at" <+/>+          text (locStrRel loc dloc) <> "." </>+          "" </>+          "Hint:" <+>+          align (textwrap "Bind the expression producing" <+> pquote (pprName d) <+>+                 "with 'let' beforehand.")++-- | Type-check a top-level (or module-level) function definition.+-- Despite the name, this is also used for checking constant+-- definitions, by treating them as 0-ary functions.+checkFunDef :: (Name, Maybe UncheckedTypeExp,+                [UncheckedTypeParam], [UncheckedPattern],+                UncheckedExp, SrcLoc)+            -> TypeM (VName, [TypeParam], [Pattern], Maybe (TypeExp VName),+                      StructType, [VName], Exp)+checkFunDef (fname, maybe_retdecl, tparams, params, body, loc) =+  fmap fst $ runTermTypeM $ do+  (tparams', params', maybe_retdecl', rettype', retext, body') <-+    checkBinding (fname, maybe_retdecl, tparams, params, body, loc)++  -- Since this is a top-level function, we also resolve overloaded+  -- types, using either defaults or complaining about ambiguities.+  fixOverloadedTypes++  -- Then replace all inferred types in the body and parameters.+  body'' <- updateTypes body'+  params'' <- updateTypes params'+  maybe_retdecl'' <- traverse updateTypes maybe_retdecl'+  rettype'' <- normTypeFully rettype'++  -- Check if pattern matches are exhaustive and yield+  -- errors if not.+  checkUnmatched body''++  -- Check if the function body can actually be evaluated.+  causalityCheck body''++  bindSpaced [(Term, fname)] $ do+    fname' <- checkName Term fname loc+    when (nameToString fname `elem` doNotShadow) $+      typeError loc mempty $+      "The" <+> pprName fname <+> "operator may not be redefined."++    return (fname', tparams', params'', maybe_retdecl'', rettype'', retext, body'')++-- | This is "fixing" as in "setting them", not "correcting them".  We+-- only make very conservative fixing.+fixOverloadedTypes :: TermTypeM ()+fixOverloadedTypes = getConstraints >>= mapM_ fixOverloaded . M.toList . M.map snd+  where fixOverloaded (v, Overloaded ots usage)+          | Signed Int32 `elem` ots = do+              unify usage (Scalar (TypeVar () Nonunique (typeName v) [])) $+                Scalar $ Prim $ Signed Int32+              warn usage "Defaulting ambiguous type to i32."+          | FloatType Float64 `elem` ots = do+              unify usage (Scalar (TypeVar () Nonunique (typeName v) [])) $+                Scalar $ Prim $ FloatType Float64+              warn usage "Defaulting ambiguous type to f64."+          | otherwise =+              typeError usage mempty $+              "Type is ambiguous (could be one of" <+> commasep (map ppr ots) <> ")." </>+              "Add a type annotation to disambiguate the type."++        fixOverloaded (_, NoConstraint _ usage) =+          typeError usage mempty $+          "Type of expression is ambiguous." </>+          "Add a type annotation to disambiguate the type."++        fixOverloaded (_, Equality usage) =+          typeError usage mempty $+          "Type is ambiguous (must be equality type)." </>+          "Add a type annotation to disambiguate the type."++        fixOverloaded (_, HasFields fs usage) =+          typeError usage mempty $+          "Type is ambiguous.  Must be record with fields:" </>+          indent 2 (stack $ map field $ M.toList fs) </>+          "Add a type annotation to disambiguate the type."+          where field (l, t) = ppr l <> colon <+> align (ppr t)++        fixOverloaded (_, HasConstrs cs usage) =+          typeError usage mempty $+          "Type is ambiguous (must be a sum type with constructors:" <+>+          ppr (Sum cs) <> ")." </>+          "Add a type annotation to disambiguate the type."++        fixOverloaded (_, Size Nothing usage) =+          typeError usage mempty "Size is ambiguous."++        fixOverloaded _ = return ()++hiddenParamNames :: [Pattern] -> Names+hiddenParamNames params = hidden+  where param_all_names = mconcat $ map patternNames params+        named (Named x, _) = Just x+        named (Unnamed, _) = Nothing+        param_names =+          S.fromList $ mapMaybe (named . patternParam) params+        hidden = param_all_names `S.difference` param_names++inferredReturnType :: SrcLoc -> [Pattern] -> PatternType -> TermTypeM StructType+inferredReturnType loc params t =+  -- The inferred type may refer to names that are bound by the+  -- parameter patterns, but which will not be visible in the type.+  -- These we must turn into fresh type variables, which will be+  -- existential in the return type.+  fmap (toStruct . fst) $+  unscopeType loc+  (M.filterWithKey (const . (`S.member` hidden)) $ foldMap patternMap params) $+  inferReturnUniqueness params t+  where hidden = hiddenParamNames params++checkBinding :: (Name, Maybe UncheckedTypeExp,+                 [UncheckedTypeParam], [UncheckedPattern],+                 UncheckedExp, SrcLoc)+             -> TermTypeM ([TypeParam], [Pattern], Maybe (TypeExp VName),+                           StructType, [VName], Exp)+checkBinding (fname, maybe_retdecl, tparams, params, body, loc) =+  noUnique $ incLevel $ bindingParams tparams params $ \tparams' params' -> do+    when (null params && any isSizeParam tparams) $+      typeError loc mempty+      "Size parameters are only allowed on bindings that also have value parameters."++    maybe_retdecl' <- forM maybe_retdecl $ \retdecl -> do+      (retdecl', ret_nodims, _) <- checkTypeExp retdecl+      (ret, _) <- instantiateEmptyArrayDims loc "funret" Nonrigid ret_nodims+      return (retdecl', ret)++    body' <- checkFunBody params' body+             (snd <$> maybe_retdecl')+             (maybe loc srclocOf maybe_retdecl)++    params'' <- mapM updateTypes params'+    body_t <- expTypeFully body'++    (maybe_retdecl'', rettype) <- case maybe_retdecl' of+      Just (retdecl', ret) -> do+        let rettype_structural = toStructural ret+        checkReturnAlias rettype_structural params'' body_t++        when (null params) $ nothingMustBeUnique loc rettype_structural++        ret' <- normTypeFully ret++        return (Just retdecl', ret')++      Nothing+        | null params ->+            return (Nothing, toStruct $ body_t `setUniqueness` Nonunique)+        | otherwise -> do+            body_t' <- inferredReturnType loc params'' body_t+            return (Nothing, body_t')++    verifyFunctionParams (Just fname) params''++    (tparams'', params''', rettype'', retext) <-+      letGeneralise fname loc tparams' params'' rettype++    checkGlobalAliases params'' body_t loc++    return (tparams'', params''', maybe_retdecl'', rettype'', retext, body')++  where -- | Check that unique return values do not alias a+        -- non-consumed parameter.+        checkReturnAlias rettp params' =+          foldM_ (checkReturnAlias' params') S.empty . returnAliasing rettp+        checkReturnAlias' params' seen (Unique, names)+          | any (`S.member` S.map snd seen) $ S.toList names =+              uniqueReturnAliased fname loc+          | otherwise = do+              notAliasingParam params' names+              return $ seen `S.union` tag Unique names+        checkReturnAlias' _ seen (Nonunique, names)+          | any (`S.member` seen) $ S.toList $ tag Unique names =+            uniqueReturnAliased fname loc+          | otherwise = return $ seen `S.union` tag Nonunique names++        notAliasingParam params' names =+          forM_ params' $ \p ->+          let consumedNonunique p' =+                not (unique $ unInfo $ identType p') && (identName p' `S.member` names)+          in case find consumedNonunique $ S.toList $ patternIdents p of+               Just p' ->+                 returnAliased fname (baseName $ identName p') loc+               Nothing ->+                 return ()++        tag u = S.map (u,)++        returnAliasing (Scalar (Record ets1)) (Scalar (Record ets2)) =+          concat $ M.elems $ M.intersectionWith returnAliasing ets1 ets2+        returnAliasing expected got =+          [(uniqueness expected, S.map aliasVar $ aliases got)]++-- | Extract all the shape names that occur in positive position+-- (roughly, left side of an arrow) in a given type.+typeDimNamesPos :: TypeBase (DimDecl VName) als -> S.Set VName+typeDimNamesPos (Scalar (Arrow _ _ t1 t2)) = onParam t1 <> typeDimNamesPos t2+  where onParam :: TypeBase (DimDecl VName) als -> S.Set VName+        onParam (Scalar Arrow{}) = mempty+        onParam (Scalar (Record fs)) = mconcat $ map onParam $ M.elems fs+        onParam (Scalar (TypeVar _ _ _ targs)) = mconcat $ map onTypeArg targs+        onParam t = typeDimNames t+        onTypeArg (TypeArgDim (NamedDim d) _) = S.singleton $ qualLeaf d+        onTypeArg (TypeArgDim _ _) = mempty+        onTypeArg (TypeArgType t _) = onParam t+typeDimNamesPos _ = mempty++checkGlobalAliases :: [Pattern] -> PatternType -> SrcLoc -> TermTypeM ()+checkGlobalAliases params body_t loc = do+  vtable <- asks $ scopeVtable . termScope+  let isLocal v = case v `M.lookup` vtable of+                    Just (BoundV Local _ _) -> True+                    _ -> False+  let als = filter (not . isLocal) $ S.toList $+            boundArrayAliases body_t `S.difference`+            foldMap patternNames params+  case als of+    v:_ | not $ null params ->+      typeError loc mempty $+      "Function result aliases the free variable " <>+      pquote (pprName v) <> "." </>+      "Use" <+> pquote "copy" <+> "to break the aliasing."+    _ ->+      return ()++inferReturnUniqueness :: [Pattern] -> PatternType -> PatternType+inferReturnUniqueness params t =+  let forbidden = aliasesMultipleTimes t+      uniques = uniqueParamNames params+      delve (Scalar (Record fs)) =+        Scalar $ Record $ M.map delve fs+      delve t'+        | all (`S.member` uniques) (boundArrayAliases t'),+          not $ any ((`S.member` forbidden) . aliasVar) (aliases t') =+            t'+        | otherwise =+            t' `setUniqueness` Nonunique+  in delve t++-- An alias inhibits uniqueness if it is used in disjoint values.+aliasesMultipleTimes :: PatternType -> Names+aliasesMultipleTimes = S.fromList . map fst . filter ((>1) . snd) . M.toList . delve+  where delve (Scalar (Record fs)) =+          foldl' (M.unionWith (+)) mempty $ map delve $ M.elems fs+        delve t =+          M.fromList $ zip (map aliasVar $ S.toList (aliases t)) $ repeat (1::Int)++uniqueParamNames :: [Pattern] -> Names+uniqueParamNames =+  S.map identName+  . S.filter (unique . unInfo . identType)+  . foldMap patternIdents++boundArrayAliases :: PatternType -> S.Set VName+boundArrayAliases (Array als _ _ _) = boundAliases als+boundArrayAliases (Scalar Prim{}) = mempty+boundArrayAliases (Scalar (Record fs)) = foldMap boundArrayAliases fs+boundArrayAliases (Scalar (TypeVar als _ _ _)) = boundAliases als+boundArrayAliases (Scalar Arrow{}) = mempty+boundArrayAliases (Scalar (Sum fs)) =+  mconcat $ concatMap (map boundArrayAliases) $ M.elems fs++-- | The set of in-scope variables that are being aliased.+boundAliases :: Aliasing -> S.Set VName+boundAliases = S.map aliasVar . S.filter bound+  where bound AliasBound{} = True+        bound AliasFree{} = False++nothingMustBeUnique :: SrcLoc -> TypeBase () () -> TermTypeM ()+nothingMustBeUnique loc = check+  where check (Array _ Unique _ _) = bad+        check (Scalar (TypeVar _ Unique _ _)) = bad+        check (Scalar (Record fs)) = mapM_ check fs+        check _ = return ()+        bad = typeError loc mempty "A top-level constant cannot have a unique type."++-- | Verify certain restrictions on function parameters, and bail out+-- on dubious constructions.+--+-- These restrictions apply to all functions (anonymous or otherwise).+-- Top-level functions have further restrictions that are checked+-- during let-generalisation.+verifyFunctionParams :: Maybe Name -> [Pattern] -> TermTypeM ()+verifyFunctionParams fname params =+  onFailure (CheckingParams fname) $+  verifyParams (foldMap patternNames params) =<< mapM updateTypes params+  where+    verifyParams forbidden (p:ps)+      | d:_ <- S.toList $ patternDimNames p `S.intersection` forbidden =+          typeError p mempty $+          "Parameter" <+> pquote (ppr p) <+/>+          "refers to size" <+> pquote (pprName d) <> comma <+/>+          textwrap "which will not be accessible to the caller" <> comma <+/>+          textwrap "possibly because it is nested in a tuple or record." <+/>+          textwrap "Consider ascribing an explicit type that does not reference " <>+          pquote (pprName d) <> "."+      | otherwise = verifyParams forbidden' ps+      where forbidden' =+              case patternParam p of+                (Named v, _) -> forbidden `S.difference` S.singleton v+                _            -> forbidden++    verifyParams _ [] = return ()++-- Returns the sizes of the immediate type produced,+-- the sizes of parameter types, and the sizes of return types.+dimUses :: StructType -> (Names, Names, Names)+dimUses = execWriter . traverseDims f+  where f PosImmediate (NamedDim v) = tell (S.singleton (qualLeaf v), mempty, mempty)+        f PosParam (NamedDim v) = tell (mempty, S.singleton (qualLeaf v), mempty)+        f PosReturn (NamedDim v) = tell (mempty, mempty, S.singleton (qualLeaf v))+        f _ _ = return ()++-- | Find at all type variables in the given type that are covered by+-- the constraints, and produce type parameters that close over them.+--+-- The passed-in list of type parameters is always prepended to the+-- produced list of type parameters.+closeOverTypes :: Name -> SrcLoc+               -> [TypeParam] -> [StructType] -> StructType+               -> Constraints -> TermTypeM ([TypeParam], StructType, [VName])+closeOverTypes defname defloc tparams paramts ret substs = do+  (more_tparams, retext) <- partitionEithers . catMaybes <$>+                            mapM closeOver (M.toList $ M.map snd to_close_over)+  let retToAnyDim v = do guard $ v `S.member` ret_sizes+                         UnknowableSize{} <- snd <$> M.lookup v substs+                         Just $ SizeSubst AnyDim+  return (tparams ++ more_tparams,+          applySubst retToAnyDim ret,+          retext)+  where t = foldFunType paramts ret+        to_close_over = M.filterWithKey (\k _ -> k `S.member` visible) substs+        visible = typeVars t <> typeDimNames t++        (produced_sizes, param_sizes, ret_sizes) = dimUses t++        -- Avoid duplicate type parameters.+        closeOver (k, _)+          | k `elem` map typeParamName tparams =+              return Nothing+        closeOver (k, NoConstraint l usage) =+          return $ Just $ Left $ TypeParamType l k $ srclocOf usage+        closeOver (k, ParamType l loc) =+          return $ Just $ Left $ TypeParamType l k loc+        closeOver (k, Size Nothing usage) =+          return $ Just $ Left $ TypeParamDim k $ srclocOf usage+        closeOver (k, UnknowableSize _ _)+          | k `S.member` param_sizes = do+              notes <- dimNotes defloc $ NamedDim $ qualName k+              typeError defloc notes $+                "Unknowable size" <+> pquote (pprName k) <+>+                "imposes constraint on type of" <+>+                pquote (pprName defname) <>+                ", which is inferred as:" </>+                indent 2 (ppr t)+          | k `S.member` produced_sizes =+              return $ Just $ Right k+        closeOver (_, _) =+          return Nothing++letGeneralise :: Name -> SrcLoc+              -> [TypeParam] -> [Pattern] -> StructType+              -> TermTypeM ([TypeParam], [Pattern], StructType, [VName])+letGeneralise defname defloc tparams params rettype =+  onFailure (CheckingLetGeneralise defname) $ do+  now_substs <- getConstraints++  -- Candidates for let-generalisation are those type variables that+  --+  -- (1) were not known before we checked this function, and+  --+  -- (2) are not used in the (new) definition of any type variables+  -- known before we checked this function.+  --+  -- (3) are not referenced from an overloaded type (for example,+  -- are the element types of an incompletely resolved record type).+  -- This is a bit more restrictive than I'd like, and SML for+  -- example does not have this restriction.+  --+  -- Criteria (1) and (2) is implemented by looking at the binding+  -- level of the type variables.+  let keep_type_vars = overloadedTypeVars now_substs++  cur_lvl <- curLevel+  let candidate k (lvl, _) = (k `S.notMember` keep_type_vars) && lvl >= cur_lvl+      new_substs = M.filterWithKey candidate now_substs++  (tparams', rettype', retext) <-+    closeOverTypes defname defloc tparams+    (map patternStructType params) rettype new_substs++  rettype'' <- updateTypes rettype'++  let used_sizes = foldMap typeDimNames $+                   rettype'' : map patternStructType params+  case filter ((`S.notMember` used_sizes) . typeParamName) $+       filter isSizeParam tparams' of+    [] -> return ()+    tp:_ -> typeError defloc mempty $+            "Size parameter" <+> pquote (ppr tp) <+> "unused."++  -- We keep those type variables that were not closed over by+  -- let-generalisation.+  modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` map typeParamName tparams'++  return (tparams', params, rettype'', retext)++checkFunBody :: [Pattern]+             -> UncheckedExp+             -> Maybe StructType+             -> SrcLoc+             -> TermTypeM Exp+checkFunBody params body maybe_rettype loc = do+  body' <- noSizeEscape $ checkExp body++  -- Unify body return type with return annotation, if one exists.+  case maybe_rettype of+    Just rettype -> do+      (rettype_withdims, _) <- instantiateEmptyArrayDims loc "impl" Nonrigid rettype++      body_t <- expTypeFully body'+      -- We need to turn any sizes provided by "hidden" parameter+      -- names into existential sizes instead.+      let hidden = hiddenParamNames params+      (body_t', _) <- unscopeType loc+                      (M.filterWithKey (const . (`S.member` hidden)) $+                       foldMap patternMap params)+                      body_t++      let usage = mkUsage (srclocOf body) "return type annotation"+      onFailure (CheckingReturn rettype (toStruct body_t')) $+        expect usage rettype_withdims $ toStruct body_t'++      -- We also have to make sure that uniqueness matches.  This is done+      -- explicitly, because uniqueness is ignored by unification.+      rettype' <- normTypeFully rettype+      body_t'' <- normTypeFully rettype -- Substs may have changed.+      unless (body_t'' `subtypeOf` anySizes rettype') $+        typeError (srclocOf body) mempty $+        "Body type" </> indent 2 (ppr body_t'') </>+        "is not a subtype of annotated type" </>+        indent 2 (ppr rettype')++    Nothing -> return ()++  return body'++--- Consumption++occur :: Occurences -> TermTypeM ()+occur = tell++-- | Proclaim that we have made read-only use of the given variable.+observe :: Ident -> TermTypeM ()+observe (Ident nm (Info t) loc) =+  let als = AliasBound nm `S.insert` aliases t+  in occur [observation als loc]++-- | Proclaim that we have written to the given variable.+consume :: SrcLoc -> Aliasing -> TermTypeM ()+consume loc als = do+  vtable <- asks $ scopeVtable . termScope+  let consumable v = case M.lookup v vtable of+                       Just (BoundV Local _ t)+                         | arrayRank t > 0 -> unique t+                         | otherwise -> True+                       _ -> False+  case filter (not . consumable) $ map aliasVar $ S.toList als of+    v:_ -> typeError loc mempty $+           "Attempt to consume variable" <+> pquote (pprName v)+           <> ", which is not allowed."+    [] -> occur [consumption als loc]++-- | Proclaim that we have written to the given variable, and mark+-- accesses to it and all of its aliases as invalid inside the given+-- computation.+consuming :: Ident -> TermTypeM a -> TermTypeM a+consuming (Ident name (Info t) loc) m = do+  consume loc $ AliasBound name `S.insert` aliases t+  localScope consume' m+  where consume' scope =+          scope { scopeVtable = M.insert name (WasConsumed loc) $ scopeVtable scope }++collectOccurences :: TermTypeM a -> TermTypeM (a, Occurences)+collectOccurences m = pass $ do+  (x, dataflow) <- listen m+  return ((x, dataflow), const mempty)++tapOccurences :: TermTypeM a -> TermTypeM (a, Occurences)+tapOccurences = listen++removeSeminullOccurences :: TermTypeM a -> TermTypeM a+removeSeminullOccurences = censor $ filter $ not . seminullOccurence++checkIfUsed :: Occurences -> Ident -> TermTypeM ()+checkIfUsed occs v+  | not $ identName v `S.member` allOccuring occs,+    not $ "_" `isPrefixOf` prettyName (identName v) =+      warn (srclocOf v) $ "Unused variable " ++ quote (pretty $ baseName $ identName v) ++ "."+  | otherwise =+      return ()++alternative :: TermTypeM a -> TermTypeM b -> TermTypeM (a,b)+alternative m1 m2 = pass $ do+  (x, occurs1) <- listen $ noSizeEscape m1+  (y, occurs2) <- listen $ noSizeEscape m2+  checkOccurences occurs1+  checkOccurences occurs2+  let usage = occurs1 `altOccurences` occurs2+  return ((x, y), const usage)++-- | Make all bindings nonunique.+noUnique :: TermTypeM a -> TermTypeM a+noUnique = localScope (\scope -> scope { scopeVtable = M.map set $ scopeVtable scope})+  where set (BoundV l tparams t)    = BoundV l tparams $ t `setUniqueness` Nonunique+        set (OverloadedF ts pts rt) = OverloadedF ts pts rt+        set EqualityF               = EqualityF+        set (WasConsumed loc)       = WasConsumed loc++onlySelfAliasing :: TermTypeM a -> TermTypeM a+onlySelfAliasing = localScope (\scope -> scope { scopeVtable = M.mapWithKey set $ scopeVtable scope})+  where set k (BoundV l tparams t)    = BoundV l tparams $+                                        t `addAliases` S.intersection (S.singleton (AliasBound k))+        set _ (OverloadedF ts pts rt) = OverloadedF ts pts rt+        set _ EqualityF               = EqualityF+        set _ (WasConsumed loc)       = WasConsumed loc++arrayOfM :: (Pretty (ShapeDecl dim), Monoid as) =>+            SrcLoc+         -> TypeBase dim as -> ShapeDecl dim -> Uniqueness+         -> TermTypeM (TypeBase dim as)+arrayOfM loc t shape u = do+  zeroOrderType (mkUsage loc "use as array element") "type used in array" t+  return $ arrayOf t shape u++updateTypes :: ASTMappable e => e -> TermTypeM e+updateTypes = astMap tv+  where tv = ASTMapper { mapOnExp         = astMap tv+                       , mapOnName        = pure+                       , mapOnQualName    = pure+                       , mapOnStructType  = normTypeFully+                       , mapOnPatternType = normTypeFully+                       }
src/Language/Futhark/TypeChecker/Types.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-} module Language.Futhark.TypeChecker.Types   ( checkTypeExp   , checkTypeDecl@@ -11,9 +13,6 @@   , checkTypeParams   , typeParamToArg -  , typeExpUses-  , checkShapeParamUses-   , TypeSub(..)   , TypeSubs   , substituteTypes@@ -24,8 +23,8 @@   ) where +import Control.Monad.Identity import Control.Monad.Reader-import Control.Monad.Except import Control.Monad.State import Data.Bifunctor import Data.List@@ -35,6 +34,8 @@  import Language.Futhark import Language.Futhark.TypeChecker.Monad+import Language.Futhark.Traversals+import Futhark.Util.Pretty  -- | @unifyTypes uf t1 t2@ attempts to unify @t1@ and @t2@.  If -- unification cannot happen, 'Nothing' is returned, otherwise a type@@ -98,14 +99,11 @@ subuniqueOf _ _              = True  checkTypeDecl :: MonadTypeChecker m =>-                 [TypeParam]-              -> TypeDeclBase NoInfo Name+                 TypeDeclBase NoInfo Name               -> m (TypeDeclBase Info VName, Liftedness)-checkTypeDecl tps (TypeDecl t NoInfo) = do+checkTypeDecl (TypeDecl t NoInfo) = do   checkForDuplicateNamesInType t   (t', st, l) <- checkTypeExp t-  let (pts, ret) = unfoldFunType st-  checkShapeParamUses tps $ pts ++ [ret]   return (TypeDecl t' $ Info st, l)  checkTypeExp :: MonadTypeChecker m =>@@ -115,9 +113,9 @@   (name', ps, t, l) <- lookupType loc name   case ps of     [] -> return (TEVar name' loc, t, l)-    _  -> throwError $ TypeError loc $-          "Type constructor " ++ quote (unwords (pretty name : map pretty ps)) ++-          " used without any arguments."+    _  -> typeError loc mempty $+          "Type constructor" <+> pquote (spread (ppr name : map ppr ps)) <+>+          "used without any arguments." checkTypeExp (TETuple ts loc) = do   (ts', ts_s, ls) <- unzip3 <$> mapM checkTypeExp ts   return (TETuple ts' loc, tupleRecord ts_s, foldl' max Unlifted ls)@@ -125,7 +123,7 @@   -- Check for duplicate field names.   let field_names = map fst fs   unless (sort field_names == sort (nub field_names)) $-    throwError $ TypeError loc $ "Duplicate record fields in " ++ pretty t+    typeError loc mempty $ "Duplicate record fields in" <+> ppr t <> "."    fs_ts_ls <- traverse checkTypeExp $ M.fromList fs   let fs' = fmap (\(x,_,_) -> x) fs_ts_ls@@ -140,11 +138,13 @@   case (l, arrayOf st (ShapeDecl [d'']) Nonunique) of     (Unlifted, st') -> return (TEArray t' d' loc, st', Unlifted)     (SizeLifted, _) ->-      throwError $ TypeError loc $-      "Cannot create array with elements of size-lifted type " ++ quote (pretty t) ++ " (might cause irregular array)."+      typeError loc mempty $+      "Cannot create array with elements of size-lifted type" <+> pquote (ppr t) <+/>+      "(might cause irregular array)."     (Lifted, _) ->-      throwError $ TypeError loc $-      "Cannot create array with elements of lifted type " ++ quote (pretty t) ++ " (might contain function)."+      typeError loc mempty $+      "Cannot create array with elements of lifted type" <+> pquote (ppr t) <+/>+      "(might contain function)."   where checkDimExp DimExpAny =           return (DimExpAny, AnyDim)         checkDimExp (DimExpConst k dloc) =@@ -182,9 +182,9 @@   (tname, tname_loc, targs) <- rootAndArgs ote   (tname', ps, t, l) <- lookupType tloc tname   if length ps /= length targs-  then throwError $ TypeError tloc $-       "Type constructor " ++ quote (pretty tname) ++ " requires " ++ show (length ps) ++-       " arguments, but provided " ++ show (length targs) ++ "."+  then typeError tloc mempty $+       "Type constructor" <+> pquote (ppr tname) <+> "requires" <+> ppr (length ps) <+>+       "arguments, but provided" <+> ppr (length targs) <+> "."   else do     (targs', substs) <- unzip <$> zipWithM checkArgApply ps targs     return (foldl (\x y -> TEApply x y tloc) (TEVar tname' tname_loc) targs',@@ -196,8 +196,8 @@         rootAndArgs (TEVar qn loc) = return (qn, loc, [])         rootAndArgs (TEApply op arg _) = do (op', loc, args) <- rootAndArgs op                                             return (op', loc, args++[arg])-        rootAndArgs te' = throwError $ TypeError (srclocOf te') $-                          "Type '" ++ pretty te' ++ "' is not a type constructor."+        rootAndArgs te' = typeError (srclocOf te') mempty $+                          "Type" <+> pquote (ppr te') <+> "is not a type constructor."          checkArgApply (TypeParamDim pv _) (TypeArgExpDim (DimExpNamed v dloc) loc) = do           v' <- checkNamedDim loc v@@ -216,16 +216,16 @@                   M.singleton pv $ TypeSub $ TypeAbbr l [] st)          checkArgApply p a =-          throwError $ TypeError tloc $ "Type argument " ++ pretty a ++-          " not valid for a type parameter " ++ pretty p+          typeError tloc mempty $ "Type argument" <+> ppr a <+>+          "not valid for a type parameter" <+> ppr p <> "."  checkTypeExp t@(TESum cs loc) = do   let constructors = map fst cs   unless (sort constructors == sort (nub constructors)) $-    throwError $ TypeError loc $ "Duplicate constructors in " ++ pretty t+    typeError loc mempty $ "Duplicate constructors in" <+> ppr t -  unless (length constructors <= 256) $-    throwError $ TypeError loc "Sum types must have 256 or fewer constructors."+  unless (length constructors < 256) $+    typeError loc mempty "Sum types must have less than 256 constructors."    cs_ts_ls <- (traverse . traverse) checkTypeExp $ M.fromList cs   let cs'  = (fmap . fmap) (\(x,_,_) -> x) cs_ts_ls@@ -253,8 +253,9 @@           already <- gets $ M.lookup v           case already of             Just prev_loc ->-              lift $ throwError $ TypeError loc $-              "Name " ++ quote (pretty v) ++ " also bound at " ++ locStr prev_loc+              lift $ typeError loc mempty $+              "Name" <+> pquote (ppr v) <+> "also bound at" <+>+              text (locStr prev_loc) <> "."             Nothing ->               modify $ M.insert v loc @@ -268,8 +269,9 @@ checkForDuplicateNamesInType = check mempty   where check seen (TEArrow (Just v) t1 t2 loc)           | Just prev_loc <- M.lookup v seen =-              throwError $ TypeError loc $-              "Name " ++ quote (pretty v) ++ " also bound at " ++ locStr prev_loc+              typeError loc mempty $+              text "Name" <+> pquote (ppr v) <+>+              "also bound at" <+> text (locStr prev_loc) <> "."           | otherwise =               check seen' t1 >> check seen' t2               where seen' = M.insert v loc seen@@ -286,38 +288,6 @@         check _ TEArray{} = return ()         check _ TEVar{} = return () --- | Ensure that every shape parameter is used in positive position at--- least once before being used in negative position.-checkShapeParamUses :: MonadTypeChecker m =>-                       [TypeParam] -> [StructType] -> m ()-checkShapeParamUses tps ts = do-  uses <- foldM onType mempty ts-  mapM_ (checkIfUsed uses) tps-  where onDim pos (NamedDim d) =-          modify $ M.insertWith min (qualLeaf d) pos-        onDim _ _ = return ()--        onType uses t = do-          let uses' = execState (traverseDims onDim t) uses-          mapM_ (checkUsage uses') tps-          return uses'--        checkUsage uses (TypeParamDim pv loc)-          | Just pos <- M.lookup pv uses,-            pos `elem` [PosParam, PosReturn] =-              throwError $ TypeError loc $-                "Shape parameter " ++ quote (prettyName pv) ++-                " must first be used in" ++-                " a positive position (non-functional parameter)."-        checkUsage _ _ = return ()--        checkIfUsed uses (TypeParamDim pv loc)-          | M.member pv uses = return ()-          | otherwise =-              throwError $ TypeError loc $ "Size parameter " ++-              quote (prettyName pv) ++ " unused."-        checkIfUsed _ _ = return ()- checkTypeParams :: MonadTypeChecker m =>                    [TypeParamBase Name]                 -> ([TypeParamBase VName] -> m a)@@ -332,9 +302,9 @@           seen <- gets $ M.lookup (ns,v)           case seen of             Just prev ->-              throwError $ TypeError loc $-              "Type parameter " ++ quote (pretty v) ++-              " previously defined at " ++ locStr prev ++ "."+              lift $ typeError loc mempty $+              text "Type parameter" <+> pquote (ppr v) <+>+              "previously defined at" <+> text (locStr prev) <> "."             Nothing -> do               modify $ M.insert (ns,v) loc               lift $ checkName ns v loc@@ -349,27 +319,7 @@ typeParamToArg (TypeParamDim v ploc) =   TypeArgDim (NamedDim $ qualName v) ploc typeParamToArg (TypeParamType _ v ploc) =-  TypeArgType (Scalar (TypeVar () Nonunique (typeName v) [])) ploc---- | Return the shapes used in a given type expression in positive and negative--- position, respectively.-typeExpUses :: TypeExp VName -> ([VName], [VName])-typeExpUses (TEVar _ _) = mempty-typeExpUses (TETuple tes _) = foldMap typeExpUses tes-typeExpUses (TERecord fs _) = foldMap (typeExpUses . snd) fs-typeExpUses (TEArray te d _) = typeExpUses te <> dimExpUses d-typeExpUses (TEUnique te _) = typeExpUses te-typeExpUses (TEApply te targ _) = typeExpUses te <> typeArgUses targ-  where typeArgUses (TypeArgExpDim d _) = dimExpUses d-        typeArgUses (TypeArgExpType tae) = typeExpUses tae-typeExpUses (TEArrow _ t1 t2 _) =-  let (pos, neg) = typeExpUses t1 <> typeExpUses t2-  in (mempty, pos <> neg)-typeExpUses (TESum cs _) = foldMap (mconcat . fmap typeExpUses . snd) cs--dimExpUses :: DimExp VName -> ([VName], [VName])-dimExpUses (DimExpNamed v _) = ([qualLeaf v], [])-dimExpUses _ = mempty+  TypeArgType (Scalar $ TypeVar () Nonunique (typeName v) []) ploc  data TypeSub = TypeSub TypeBinding              | DimSub (DimDecl VName)@@ -428,45 +378,59 @@ -- substitution (but which is certainly an overloaded primitive -- type!).  The latter is used to remove aliases from types that are -- yet-unknown but that we know cannot carry aliases (see issue #682).-data Subst t = Subst t | PrimSubst+data Subst t = Subst t | PrimSubst | SizeSubst (DimDecl VName)+  deriving (Show)  instance Functor Subst where   fmap f (Subst t) = Subst $ f t   fmap _ PrimSubst = PrimSubst+  fmap _ (SizeSubst v) = SizeSubst v  -- | Class of types which allow for substitution of types with no -- annotations for type variable names. class Substitutable a where-  applySubst :: (VName -> Maybe (Subst (TypeBase () ()))) -> a -> a+  applySubst :: (VName -> Maybe (Subst StructType)) -> a -> a -instance Substitutable (TypeBase () ()) where+instance Substitutable (TypeBase (DimDecl VName) ()) where   applySubst = substTypesAny -instance Substitutable (TypeBase () Aliasing) where+instance Substitutable (TypeBase (DimDecl VName) Aliasing) where   applySubst = substTypesAny . (fmap (fmap fromStruct).) -instance Substitutable (TypeBase (DimDecl VName) ()) where-  applySubst = substTypesAny . (fmap (fmap vacuousShapeAnnotations).)+instance Substitutable (DimDecl VName) where+  applySubst f (NamedDim (QualName _ v))+    | Just (SizeSubst d) <- f v = d+  applySubst _ d = d -instance Substitutable (TypeBase (DimDecl VName) Aliasing) where-  applySubst = substTypesAny . (fmap (fmap (vacuousShapeAnnotations . fromStruct)).)+instance Substitutable d => Substitutable (ShapeDecl d) where+  applySubst f = fmap $ applySubst f +instance Substitutable Pattern where+  applySubst f = runIdentity . astMap mapper+    where mapper = ASTMapper { mapOnExp = return+                             , mapOnName = return+                             , mapOnQualName = return+                             , mapOnStructType = return . applySubst f+                             , mapOnPatternType = return . applySubst f+                             }+ -- | Perform substitutions, from type names to types, on a type. Works -- regardless of what shape and uniqueness information is attached to the type.-substTypesAny :: (ArrayDim dim, Monoid as) =>-                 (VName -> Maybe (Subst (TypeBase dim as)))-              -> TypeBase dim as -> TypeBase dim as+substTypesAny :: Monoid as =>+                 (VName -> Maybe (Subst (TypeBase (DimDecl VName) as)))+              -> TypeBase (DimDecl VName) as -> TypeBase (DimDecl VName) as substTypesAny lookupSubst ot = case ot of   Array als u et shape ->-    arrayOf (substTypesAny lookupSubst' (Scalar et)) shape u `setAliases` als+    arrayOf (substTypesAny lookupSubst' (Scalar et))+    (applySubst lookupSubst' shape) u `setAliases` als   Scalar (Prim t) -> Scalar $ Prim t   -- We only substitute for a type variable with no arguments, since   -- type parameters cannot have higher kind.   Scalar (TypeVar als u v targs) ->     case lookupSubst $ qualLeaf (qualNameFromTypeName v) of-      Just (Subst t) -> t `setUniqueness` u `addAliases` (<>als)+      Just (Subst t) -> substTypesAny lookupSubst $ t `setUniqueness` u `addAliases` (<>als)       Just PrimSubst -> Scalar $ TypeVar mempty u v $ map subsTypeArg targs-      Nothing -> Scalar $ TypeVar als u v $ map subsTypeArg targs+      _ -> Scalar $ TypeVar als u v $ map subsTypeArg targs   Scalar (Record ts) -> Scalar $ Record $ fmap (substTypesAny lookupSubst) ts   Scalar (Arrow als v t1 t2) ->     Scalar $ Arrow als v (substTypesAny lookupSubst t1) (substTypesAny lookupSubst t2)@@ -475,6 +439,7 @@    where subsTypeArg (TypeArgType t loc) =           TypeArgType (substTypesAny lookupSubst' t) loc-        subsTypeArg t = t+        subsTypeArg (TypeArgDim v loc) =+          TypeArgDim (applySubst lookupSubst' v) loc          lookupSubst' = fmap (fmap $ second (const ())) . lookupSubst
src/Language/Futhark/TypeChecker/Unify.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-} module Language.Futhark.TypeChecker.Unify   ( Constraint(..)   , Usage@@ -7,10 +9,13 @@   , mkUsage'   , Level   , Constraints-  , lookupSubst   , MonadUnify(..)-  , BreadCrumb(..)-  , typeError+  , Rigidity(..)+  , RigidSource(..)+  , BreadCrumbs+  , noBreadCrumbs+  , hasNoBreadCrumbs+  , dimNotes   , mkTypeVarName    , zeroOrderType@@ -18,28 +23,76 @@   , mustHaveField   , mustBeOneOf   , equalityType-  , normaliseType+  , normType+  , normPatternType+  , normTypeFully+  , instantiateEmptyArrayDims    , unify+  , expect+  , unifyMostCommon+  , anyDimOnMismatch   , doUnification   ) where  import Control.Monad.Except+import Control.Monad.Writer hiding (Sum)+import Control.Monad.RWS.Strict hiding (Sum) import Control.Monad.State+import Data.Bifoldable (biany) import Data.List import Data.Loc import Data.Maybe import qualified Data.Map.Strict as M import qualified Data.Set as S -import Language.Futhark+import Language.Futhark hiding (unifyDims) import Language.Futhark.TypeChecker.Monad hiding (BoundV) import Language.Futhark.TypeChecker.Types-import Futhark.Util.Pretty (Pretty)+import Futhark.Util.Pretty hiding (empty) +-- | A piece of information that describes what process the type+-- checker currently performing.  This is used to give better error+-- messages for unification errors.+data BreadCrumb = MatchingTypes StructType StructType+                | MatchingFields [Name]+                | MatchingConstructor Name+                | Matching Doc++instance Pretty BreadCrumb where+  ppr (MatchingTypes t1 t2) =+    "When matching type" </> indent 2 (ppr t1) </>+    "with" </> indent 2 (ppr t2)+  ppr (MatchingFields fields) =+    "When matching types of record field" <+>+    pquote (mconcat $ punctuate "." $ map ppr fields) <> dot+  ppr (MatchingConstructor c) =+    "When matching types of constructor" <+> pquote (ppr c) <> dot+  ppr (Matching s) =+    s++newtype BreadCrumbs = BreadCrumbs [BreadCrumb]++noBreadCrumbs :: BreadCrumbs+noBreadCrumbs = BreadCrumbs []++hasNoBreadCrumbs :: BreadCrumbs -> Bool+hasNoBreadCrumbs (BreadCrumbs xs) = null xs++breadCrumb :: BreadCrumb -> BreadCrumbs -> BreadCrumbs+breadCrumb (MatchingFields xs) (BreadCrumbs (MatchingFields ys : bcs)) =+  BreadCrumbs $ MatchingFields (ys++xs) : bcs+breadCrumb bc (BreadCrumbs bcs) =+  BreadCrumbs $ bc : bcs++instance Pretty BreadCrumbs where+  ppr (BreadCrumbs []) = mempty+  ppr (BreadCrumbs bcs) = line <> stack (map ppr bcs)+ -- | A usage that caused a type constraint. data Usage = Usage (Maybe String) SrcLoc+  deriving (Show)  mkUsage :: SrcLoc -> String -> Usage mkUsage = flip (Usage . Just)@@ -47,9 +100,9 @@ mkUsage' :: SrcLoc -> Usage mkUsage' = Usage Nothing -instance Show Usage where-  show (Usage Nothing loc) = "use at " ++ locStr loc-  show (Usage (Just s) loc) = s ++ " at " ++ locStr loc+instance Pretty Usage where+  ppr (Usage Nothing loc) = "use at " <> textwrap (locStr loc)+  ppr (Usage (Just s) loc) = textwrap s <+/> "at" <+> textwrap (locStr loc)  instance Located Usage where   locOf (Usage _ loc) = locOf loc@@ -61,21 +114,33 @@  data Constraint = NoConstraint Liftedness Usage                 | ParamType Liftedness SrcLoc-                | Constraint (TypeBase () ()) Usage+                | Constraint StructType Usage                 | Overloaded [PrimType] Usage-                | HasFields (M.Map Name (TypeBase () ())) Usage+                | HasFields (M.Map Name StructType) Usage                 | Equality Usage-                | HasConstrs (M.Map Name [TypeBase () ()]) Usage+                | HasConstrs (M.Map Name [StructType]) Usage+                | ParamSize SrcLoc+                | Size (Maybe (DimDecl VName)) Usage+                  -- ^ Is not actually a type, but a term-level size,+                  -- possibly already set to something specific.+                | UnknowableSize SrcLoc RigidSource+                  -- ^ A size that does not unify with anything -+                  -- created from the result of applying a function+                  -- whose return size is existential, or otherwise+                  -- hiding a size.                 deriving Show  instance Located Constraint where   locOf (NoConstraint _ usage) = locOf usage-  locOf (ParamType _ loc) = locOf loc+  locOf (ParamType _ usage) = locOf usage   locOf (Constraint _ usage) = locOf usage   locOf (Overloaded _ usage) = locOf usage   locOf (HasFields _ usage) = locOf usage   locOf (Equality usage) = locOf usage   locOf (HasConstrs _ usage) = locOf usage+  locOf (ParamSize loc) = locOf loc+  locOf (Size _ usage) = locOf usage+  locOf (UnknowableSize loc _) = locOf loc  -- | Mapping from fresh type variables, instantiated from the type -- schemes of polymorphic functions, to (possibly) specific types as@@ -83,13 +148,104 @@ -- a partial constraint on their type. type Constraints = M.Map VName (Level, Constraint) -lookupSubst :: VName -> Constraints -> Maybe (Subst (TypeBase () ()))-lookupSubst v constraints = case M.lookup v constraints of-                              Just (_, Constraint t _) -> Just $ Subst t-                              Just (_, Overloaded{}) -> Just PrimSubst+lookupSubst :: VName -> Constraints -> Maybe (Subst StructType)+lookupSubst v constraints = case snd <$> M.lookup v constraints of+                              Just (Constraint t _) -> Just $ Subst t+                              Just Overloaded{} -> Just PrimSubst+                              Just (Size (Just d) _) ->+                                Just $ SizeSubst $ applySubst (`lookupSubst` constraints) d                               _ -> Nothing -class (MonadBreadCrumbs m, MonadError TypeError m) => MonadUnify m where+-- | The source of a rigid size.+data RigidSource+  = RigidArg (Maybe (QualName VName)) String+    -- ^ A function argument that is not a constant or variable name.+  | RigidRet (Maybe (QualName VName))+    -- ^ An existential return size.+  | RigidLoop+  | RigidSlice (Maybe (DimDecl VName)) String+    -- ^ Produced by a complicated slice expression.+  | RigidRange+    -- ^ Produced by a complicated range expression.+  | RigidBound String+    -- ^ Produced by a range expression with this bound.+  | RigidCond StructType StructType+    -- ^ Mismatch in branches.+  | RigidUnify+    -- ^ Invented during unification.+  | RigidOutOfScope SrcLoc VName+  deriving (Eq, Ord, Show)++-- | The ridigity of a size variable.  All rigid sizes are tagged with+-- information about how they were generated.+data Rigidity = Rigid RigidSource | Nonrigid+              deriving (Eq, Ord, Show)++prettySource :: SrcLoc -> SrcLoc -> RigidSource -> Doc++prettySource ctx loc (RigidRet Nothing) =+  "is unknown size returned by function at" <+>+  text (locStrRel ctx loc) <> "."++prettySource ctx loc (RigidRet (Just fname)) =+  "is unknown size returned by" <+> pquote (ppr fname) <+>+  "at" <+> text (locStrRel ctx loc) <> "."++prettySource ctx loc (RigidArg fname arg) =+  "is value of argument" </>+  indent 2 (shorten arg) </>+  "passed to" <+> fname' <+> "at" <+> text (locStrRel ctx loc) <> "."+  where fname' = maybe "function" (pquote . ppr) fname++prettySource ctx loc (RigidSlice d slice) =+  "is size produced by slice" </>+  indent 2 (shorten slice) </>+  d_desc <> "at" <+> text (locStrRel ctx loc) <> "."+  where d_desc = case d of+                   Just d' -> "of dimension of size " <> pquote (ppr d') <> " "+                   Nothing -> mempty++prettySource ctx loc RigidLoop =+  "is unknown size of value returned at" <+> text (locStrRel ctx loc) <> "."++prettySource ctx loc RigidRange =+  "is unknown length of range at" <+> text (locStrRel ctx loc) <> "."++prettySource ctx loc (RigidBound bound) =+  "generated from expression" </>+  indent 2 (shorten bound) </>+  "used in range at " <> text (locStrRel ctx loc) <> "."++prettySource ctx loc (RigidOutOfScope boundloc v) =+  "is an unknown size arising from " <> pquote (pprName v) <>+  " going out of scope at " <> text (locStrRel ctx loc) <> "." </>+  "Originally bound at " <> text (locStrRel ctx boundloc) <> "."++prettySource _ _ RigidUnify =+  "is an artificial size invented during unification of functions with anonymous sizes"++prettySource ctx loc (RigidCond t1 t2) =+  "is unknown due to conditional expression at " <>+  text (locStrRel ctx loc) <> "." </>+  "One branch returns array of type: " <> align (ppr t1) </>+  "The other an array of type:       " <> align (ppr t2)++dimNotes :: (Located a, MonadUnify m) => a -> DimDecl VName -> m Notes+dimNotes ctx (NamedDim d) = do+  c <- M.lookup (qualLeaf d) <$> getConstraints+  case c of+    Just (_, UnknowableSize loc rsrc) ->+      return $ aNote $ pretty $+      pquote (ppr d) <+> prettySource (srclocOf ctx) loc rsrc+    _ -> return mempty+dimNotes _ _ = return mempty++typeNotes :: (Located a, MonadUnify m) => a -> StructType -> m Notes+typeNotes ctx =+  fmap mconcat . mapM (dimNotes ctx . NamedDim . qualName) .+  S.toList . typeDimNames++class Monad m => MonadUnify m where   getConstraints :: m Constraints   putConstraints :: Constraints -> m ()   modifyConstraints :: (Constraints -> Constraints) -> m ()@@ -98,17 +254,56 @@     putConstraints $ f x    newTypeVar :: Monoid als => SrcLoc -> String -> m (TypeBase dim als)+  newDimVar :: SrcLoc -> Rigidity -> String -> m VName    curLevel :: m Level -normaliseType :: (Substitutable a, MonadUnify m) => a -> m a-normaliseType t = do constraints <- getConstraints+  matchError :: Located loc => loc -> Notes -> BreadCrumbs+             -> StructType -> StructType -> m a++  unifyError :: Located loc => loc -> Notes -> BreadCrumbs+             -> Doc -> m a++normTypeFully :: (Substitutable a, MonadUnify m) => a -> m a+normTypeFully t = do constraints <- getConstraints                      return $ applySubst (`lookupSubst` constraints) t +normType :: MonadUnify m => StructType -> m StructType+normType t@(Scalar (TypeVar _ _ (TypeName [] v) [])) = do+  constraints <- getConstraints+  case snd <$> M.lookup v constraints of+    Just (Constraint t' _) -> normType t'+    _ -> return t+normType t = return t++normPatternType :: MonadUnify m => PatternType -> m PatternType+normPatternType t@(Scalar (TypeVar als u (TypeName [] v) [])) = do+  constraints <- getConstraints+  case snd <$> M.lookup v constraints of+    Just (Constraint t' _) ->+      normPatternType $ t' `setUniqueness` u `setAliases` als+    _ -> return t+normPatternType t = return t+ rigidConstraint :: Constraint -> Bool rigidConstraint ParamType{} = True+rigidConstraint ParamSize{} = True+rigidConstraint UnknowableSize{} = True rigidConstraint _ = False +instantiateEmptyArrayDims :: MonadUnify m =>+                             SrcLoc -> String -> Rigidity+                          -> TypeBase (DimDecl VName) als+                          -> m (TypeBase (DimDecl VName) als, [VName])+instantiateEmptyArrayDims tloc desc r = runWriterT . traverseDims onDim+  where onDim PosImmediate AnyDim = inst+        onDim PosParam AnyDim = inst+        onDim _ d = return d+        inst = do+          dim <- lift $ newDimVar tloc r desc+          tell [dim]+          return $ NamedDim $ qualName dim+ -- | Is the given type variable the name of an abstract type or type -- parameter, which we cannot substitute? isRigid :: VName -> Constraints -> Bool@@ -122,149 +317,236 @@   guard $ not $ rigidConstraint c   return lvl -unifySharedConstructors :: MonadUnify m =>-                           Usage-                        -> M.Map Name [TypeBase () ()]-                        -> M.Map Name [TypeBase () ()]-                        -> m ()-unifySharedConstructors usage cs1 cs2 =-  forM_ (M.toList $ M.intersectionWith (,) cs1 cs2) $ \(c, (f1, f2)) ->-  unifyConstructor c f1 f2-  where unifyConstructor c f1 f2-          | length f1 == length f2 =-              zipWithM_ (unify usage) f1 f2-          | otherwise = typeError usage $ "Cannot unify constructor " ++-                        quote (prettyName c) ++ "."--indent :: String -> String-indent = intercalate "\n" . map ("  "++) . lines+type UnifyDims m =+  BreadCrumbs -> [VName] -> (VName -> Maybe Int) -> DimDecl VName -> DimDecl VName -> m () --- | Unifies two types.-unify :: MonadUnify m => Usage -> TypeBase () () -> TypeBase () () -> m ()-unify usage orig_t1 orig_t2 = do-  orig_t1' <- normaliseType orig_t1-  orig_t2' <- normaliseType orig_t2-  breadCrumb (MatchingTypes orig_t1' orig_t2') $ subunify orig_t1' orig_t2'+unifyWith :: MonadUnify m =>+             UnifyDims m -> Usage -> BreadCrumbs+          -> StructType -> StructType -> m ()+unifyWith onDims usage = subunify False mempty   where-    subunify t1 t2 = do+    swap True x y = (y, x)+    swap False x y = (x, y)++    subunify ord bound bcs t1 t2 = do       constraints <- getConstraints +      t1' <- normType t1+      t2' <- normType t2+       let nonrigid v = isNonRigid v constraints-          t1' = applySubst (`lookupSubst` constraints) t1-          t2' = applySubst (`lookupSubst` constraints) t2 -          failure-            -- This case is to avoid repeating the types that are also-            -- shown in the breadcrumb.-            | t1 == orig_t1, t2 == orig_t2 =-                typeError (srclocOf usage) "Types do not match."-            | otherwise =-                typeError (srclocOf usage) $ "Couldn't match expected type\n" ++-                indent (pretty t1') ++ "\nwith actual type\n" ++ indent (pretty t2')+          failure = matchError (srclocOf usage) mempty bcs t1' t2' -      case (t1', t2') of-        _ | t1' == t2' -> return ()+          -- Remove any of the intermediate dimensions we added just+          -- for unification purposes.+          link v lvl = linkVarToType onDims usage bcs v lvl . applySubst unbind+            where unbind d | d `elem` bound = Just $ SizeSubst AnyDim+                           | otherwise      = Nothing +          unifyTypeArg bcs' (TypeArgDim d1 _) (TypeArgDim d2 _) =+            onDims' bcs' (swap ord d1 d2)+          unifyTypeArg bcs' (TypeArgType t _) (TypeArgType arg_t _) =+            subunify ord bound bcs' t arg_t+          unifyTypeArg bcs' _ _ = unifyError usage mempty bcs'+            "Cannot unify a type argument with a dimension argument (or vice versa)."++          onDims' bcs' (d1, d2) =+            onDims bcs' bound nonrigid+            (applySubst (`lookupSubst` constraints) d1)+            (applySubst (`lookupSubst` constraints) d2)++      case (t1', t2') of         (Scalar (Record fs),          Scalar (Record arg_fs))           | M.keys fs == M.keys arg_fs ->-              forM_ (M.toList $ M.intersectionWith (,) fs arg_fs) $ \(k, (k_t1, k_t2)) ->-              breadCrumb (MatchingFields k) $ subunify k_t1 k_t2+              forM_ (M.toList $ M.intersectionWith (,) fs arg_fs) $ \(k, (k_t1, k_t2)) -> do+              let bcs' = breadCrumb (MatchingFields [k]) bcs+              subunify ord bound bcs' k_t1 k_t2+          | otherwise -> do+              let missing = filter (`notElem` M.keys arg_fs) (M.keys fs) +++                            filter (`notElem` M.keys fs) (M.keys arg_fs)+              unifyError usage mempty bcs $+                "Unshared fields:" <+> commasep (map ppr missing) <> "."          (Scalar (TypeVar _ _ (TypeName _ tn) targs),          Scalar (TypeVar _ _ (TypeName _ arg_tn) arg_targs))-          | tn == arg_tn, length targs == length arg_targs ->-              zipWithM_ unifyTypeArg targs arg_targs+          | tn == arg_tn, length targs == length arg_targs -> do+            let bcs' = breadCrumb (Matching "When matching type arguments.") bcs+            zipWithM_ (unifyTypeArg bcs') targs arg_targs          (Scalar (TypeVar _ _ (TypeName [] v1) []),          Scalar (TypeVar _ _ (TypeName [] v2) [])) ->           case (nonrigid v1, nonrigid v2) of             (Nothing, Nothing) -> failure-            (Just lvl1, Nothing) -> linkVarToType usage v1 lvl1 t2'-            (Nothing, Just lvl2) -> linkVarToType usage v2 lvl2 t1'+            (Just lvl1, Nothing) -> link v1 lvl1 t2'+            (Nothing, Just lvl2) -> link v2 lvl2 t1'             (Just lvl1, Just lvl2)-              | lvl1 <= lvl2 -> linkVarToType usage v1 lvl1 t2'-              | otherwise    -> linkVarToType usage v2 lvl2 t1'+              | lvl1 <= lvl2 -> link v1 lvl1 t2'+              | otherwise    -> link v2 lvl2 t1'          (Scalar (TypeVar _ _ (TypeName [] v1) []), _)-          | Just lvl <-  nonrigid v1 ->-              linkVarToType usage v1 lvl t2'+          | Just lvl <- nonrigid v1 ->+              link v1 lvl t2'         (_, Scalar (TypeVar _ _ (TypeName [] v2) []))           | Just lvl <- nonrigid v2 ->-              linkVarToType usage v2 lvl t1'+              link v2 lvl t1' -        (Scalar (Arrow _ _ a1 b1),-         Scalar (Arrow _ _ a2 b2)) -> do-          subunify a1 a2-          subunify b1 b2+        (Scalar (Arrow _ p1 a1 b1),+         Scalar (Arrow _ p2 a2 b2)) -> do+          let (r1, r2) = swap ord (Rigid RigidUnify) Nonrigid+          (a1', a1_dims) <- instantiateEmptyArrayDims (srclocOf usage) "anonymous" r1 a1+          (a2', a2_dims) <- instantiateEmptyArrayDims (srclocOf usage) "anonymous" r2 a2+          let bound' = bound <> mapMaybe pname [p1, p2] <> a1_dims <> a2_dims+          subunify (not ord) bound+            (breadCrumb (Matching "When matching parameter types.") bcs)+            a1' a2'+          subunify ord bound'+            (breadCrumb (Matching "When matching return types.") bcs)+            b1' b2'+          where (b1', b2') =+                  -- Replace one parameter name with the other in the+                  -- return type, in case of dependent types.  I.e.,+                  -- we want type '(n: i32) -> [n]i32' to unify with+                  -- type '(x: i32) -> [x]i32'.+                  case (p1, p2) of+                    (Named p1', Named p2') ->+                      let f v | v == p2' = Just $ SizeSubst $ NamedDim $ qualName p1'+                              | otherwise = Nothing+                      in (b1, applySubst f b2) +                    (_, _) ->+                      (b1, b2)++                pname (Named x) = Just x+                pname Unnamed = Nothing+         (Array{}, Array{})-          | Just t1'' <- peelArray 1 t1',-            Just t2'' <- peelArray 1 t2' ->-              subunify t1'' t2''+          | ShapeDecl (t1_d : _) <- arrayShape t1',+            ShapeDecl (t2_d : _) <- arrayShape t2',+            Just t1'' <- peelArray 1 t1',+            Just t2'' <- peelArray 1 t2' -> do+              onDims' bcs (swap ord t1_d t2_d)+              subunify ord bound bcs t1'' t2''          (Scalar (Sum cs),          Scalar (Sum arg_cs))           | M.keys cs == M.keys arg_cs ->-              unifySharedConstructors usage cs arg_cs-        (_, _) -> failure+              unifySharedConstructors onDims usage bcs cs arg_cs+          | otherwise -> do+              let missing = filter (`notElem` M.keys arg_cs) (M.keys cs) +++                            filter (`notElem` M.keys cs) (M.keys arg_cs)+              unifyError usage mempty bcs $+                "Unshared constructors:" <+> commasep (map (("#"<>) . ppr) missing) <> "." -      where unifyTypeArg TypeArgDim{} TypeArgDim{} = return ()-            unifyTypeArg (TypeArgType t _) (TypeArgType arg_t _) =-              subunify t arg_t-            unifyTypeArg _ _ = typeError usage-              "Cannot unify a type argument with a dimension argument (or vice versa)."+        _ | t1' == t2' -> return ()+          | otherwise -> failure -applySubstInConstraint :: VName -> Subst (TypeBase () ()) -> Constraint -> Constraint-applySubstInConstraint vn subst (Constraint t loc) =-  Constraint (applySubst (flip M.lookup $ M.singleton vn subst) t) loc-applySubstInConstraint vn subst (HasFields fs loc) =-  HasFields (M.map (applySubst (flip M.lookup $ M.singleton vn subst)) fs) loc-applySubstInConstraint _ _ (NoConstraint l loc) = NoConstraint l loc-applySubstInConstraint _ _ (Overloaded ts usage) = Overloaded ts usage-applySubstInConstraint _ _ (Equality loc) = Equality loc-applySubstInConstraint _ _ (ParamType l loc) = ParamType l loc-applySubstInConstraint vn subst (HasConstrs cs loc) =-  HasConstrs (M.map (map (applySubst (flip M.lookup $ M.singleton vn subst))) cs) loc+unifyDims :: MonadUnify m => Usage -> UnifyDims m+unifyDims _ _ _ _ d1 d2+  | d1 == d2 = return ()+unifyDims usage bcs _ nonrigid (NamedDim (QualName _ d1)) d2+  | Just lvl1 <- nonrigid d1 =+      linkVarToDim usage bcs d1 lvl1 d2+unifyDims usage bcs _ nonrigid d1 (NamedDim (QualName _ d2))+  | Just lvl2 <- nonrigid d2 =+      linkVarToDim usage bcs d2 lvl2 d1+unifyDims usage bcs _ _ d1 d2 = do+  notes <- (<>) <$> dimNotes usage d1 <*> dimNotes usage d2+  unifyError usage notes bcs $+    "Dimensions" <+> pquote (ppr d1) <+>+    "and" <+> pquote (ppr d2) <+> "do not match." -occursCheck :: MonadUnify m => Usage -> VName -> TypeBase () () -> m ()-occursCheck usage vn tp =+-- | Unifies two types.+unify :: MonadUnify m => Usage -> StructType -> StructType -> m ()+unify usage = unifyWith (unifyDims usage) usage noBreadCrumbs++-- | @expect super sub@ checks that @sub@ is a subtype of @super@.+expect :: MonadUnify m => Usage -> StructType -> StructType -> m ()+expect usage = unifyWith onDims usage noBreadCrumbs+  where onDims _ _ _ AnyDim _ = return ()+        onDims _ _ _ d1 d2+          | d1 == d2 = return ()+        onDims bcs bound nonrigid (NamedDim (QualName _ d1)) d2+          | Just lvl1 <- nonrigid d1, d2 /= AnyDim, not $ boundParam bound d2 =+              linkVarToDim usage bcs d1 lvl1 d2+        onDims bcs bound nonrigid d1 (NamedDim (QualName _ d2))+          | Just lvl2 <- nonrigid d2, not $ boundParam bound d1 =+              linkVarToDim usage bcs d2 lvl2 d1+        onDims bcs _ _ d1 d2 = do+          notes <- (<>) <$> dimNotes usage d1 <*> dimNotes usage d2+          unifyError usage notes bcs $ "Dimensions" <+> pquote (ppr d1) <+>+            "and" <+> pquote (ppr d2) <+> "do not match."++        boundParam bound (NamedDim (QualName _ d)) = d `elem` bound+        boundParam _ _ = False++hasEmptyDims :: StructType -> Bool+hasEmptyDims = biany empty (const False)+  where empty AnyDim = True+        empty _ = False++occursCheck :: MonadUnify m =>+               Usage -> BreadCrumbs+            -> VName -> StructType -> m ()+occursCheck usage bcs vn tp =   when (vn `S.member` typeVars tp) $-  typeError usage $ "Occurs check: cannot instantiate " ++-  prettyName vn ++ " with " ++ pretty tp+  unifyError usage mempty bcs $ "Occurs check: cannot instantiate" <+>+  pprName vn <+> "with" <+> ppr tp <> "." -scopeCheck :: MonadUnify m => Usage -> VName -> Level -> TypeBase () () -> m ()-scopeCheck usage vn max_lvl tp = do+scopeCheck :: MonadUnify m =>+              Usage -> BreadCrumbs+           -> VName -> Level -> StructType -> m ()+scopeCheck usage bcs vn max_lvl tp = do   constraints <- getConstraints-  mapM_ (check constraints) $ typeVars tp-  where check constraints v+  checkType constraints tp+  where checkType constraints t =+          mapM_ (check constraints) $ typeVars t <> typeDimNames t++        check constraints v           | Just (lvl, c) <- M.lookup v constraints,             lvl > max_lvl =               if rigidConstraint c               then scopeViolation v               else modifyConstraints $ M.insert v (max_lvl, c)+           | otherwise =               return () -        scopeViolation v =-          typeError usage $ "Cannot unify type variable " ++ quote (prettyName v) ++-          " with " ++ quote (prettyName vn) ++ " (scope violation).\n" ++-          "This is because " ++ quote (prettyName vn) ++ " is rigidly bound in a deeper scope."+        scopeViolation v = do+          notes <- typeNotes usage tp+          unifyError usage notes bcs $ "Cannot unify type" </>+            indent 2 (ppr tp) </>+            "with" <+> pquote (pprName vn) <+> "(scope violation)." </>+            "This is because" <+> pquote (pprName v) <+>+            "is rigidly bound in a deeper scope." -linkVarToType :: MonadUnify m => Usage -> VName -> Level -> TypeBase () () -> m ()-linkVarToType usage vn lvl tp = do-  occursCheck usage vn tp-  scopeCheck usage vn lvl tp+linkVarToType :: MonadUnify m =>+                 UnifyDims m -> Usage -> BreadCrumbs+              -> VName -> Level -> StructType -> m ()+linkVarToType onDims usage bcs vn lvl tp = do+  occursCheck usage bcs vn tp+  scopeCheck usage bcs vn lvl tp    constraints <- getConstraints+  let tp' = removeUniqueness tp   modifyConstraints $ M.insert vn (lvl, Constraint tp' usage)-  modifyConstraints $ M.map $ fmap $ applySubstInConstraint vn $ Subst tp'-   case snd <$> M.lookup vn constraints of -    Just (NoConstraint Unlifted unlift_usage) ->-      zeroOrderType usage (show unlift_usage) tp'+    Just (NoConstraint Unlifted unlift_usage) -> do+      let bcs' = breadCrumb+                 (Matching $ "When verifying that" <+> pquote (pprName vn) <+>+                  textwrap "is not instantiated with a function type, due to" <+>+                  ppr unlift_usage)+                 bcs+      zeroOrderTypeWith usage bcs' tp' +      when (hasEmptyDims tp') $+        unifyError usage mempty bcs $ "Type variable" <+> pprName vn <+>+        "cannot be instantiated with type containing anonymous sizes:" </>+        indent 2 (ppr tp) </>+        textwrap "This is usually because the size of an array returned by a higher-order function argument cannot be determined statically.  This can also be due to the return size beind a value parameter.  Add type annotation to clarify."+     Just (Equality _) ->       equalityType usage tp' @@ -275,11 +557,11 @@               | not $ isRigid v constraints ->                   linkVarToTypes usage v ts             _ ->-              typeError usage $ "Cannot unify " ++ quote (prettyName vn) ++-              "' with type\n" ++ indent (pretty tp) ++ "\nas " ++-              quote (prettyName vn) ++ " must be one of " ++-              intercalate ", " (map pretty ts) ++-              " due to " ++ show old_usage ++ ")."+              unifyError usage mempty bcs $ "Cannot instantiate" <+> pquote (pprName vn) <+>+              "with type" </> indent 2 (ppr tp) </> "as" <+>+              pquote (pprName vn) <+> "must be one of" <+>+              commasep (map ppr ts) <+/>+              "due to" <+/> ppr old_usage <> "."      Just (HasFields required_fields old_usage) ->       case tp of@@ -292,35 +574,59 @@               modifyConstraints $ M.insert v               (lvl, HasFields required_fields old_usage)         _ ->-          typeError usage $-          "Cannot unify " ++ quote (prettyName vn) ++ " with type\n" ++-          indent (pretty tp) ++ "\nas " ++ quote (prettyName vn) ++-          " must be a record with fields\n" ++-          pretty (Record required_fields) ++-          "\ndue to " ++ show old_usage ++ "."+          unifyError usage mempty bcs $+          "Cannot instantiate" <+> pquote (pprName vn) <+> "with type" </>+          indent 2 (ppr tp) </>+          "as" <+> pquote (pprName vn) <+> "must be a record with fields" </>+          indent 2 (ppr (Record required_fields)) </>+          "due to" <+> ppr old_usage <> "."      Just (HasConstrs required_cs old_usage) ->       case tp of         Scalar (Sum ts)           | all (`M.member` ts) $ M.keys required_cs ->-              unifySharedConstructors usage required_cs ts+              unifySharedConstructors onDims usage bcs required_cs ts         Scalar (TypeVar _ _ (TypeName [] v) [])           | not $ isRigid v constraints -> do               case M.lookup v constraints of                 Just (_, HasConstrs v_cs _) ->-                  unifySharedConstructors usage required_cs v_cs+                  unifySharedConstructors onDims usage bcs required_cs v_cs                 _ -> return ()               modifyConstraints $ M.insertWith combineConstrs v                 (lvl, HasConstrs required_cs old_usage)               where combineConstrs (_, HasConstrs cs1 usage1) (_, HasConstrs cs2 _) =                       (lvl, HasConstrs (M.union cs1 cs2) usage1)                     combineConstrs hasCs _ = hasCs-        _ -> typeError usage "Cannot unify a sum type with a non-sum type"+        _ -> noSumType      _ -> return () -  where tp' = removeUniqueness tp+  where noSumType = unifyError usage mempty bcs+                    "Cannot unify a sum type with a non-sum type" +linkVarToDim :: MonadUnify m =>+                Usage -> BreadCrumbs+             -> VName -> Level -> DimDecl VName -> m ()+linkVarToDim usage bcs vn lvl dim = do+  constraints <- getConstraints++  case dim of+    NamedDim dim'+      | Just (dim_lvl, c) <- qualLeaf dim' `M.lookup` constraints,+        dim_lvl > lvl ->+          case c of+            ParamSize{} -> do+              notes <- dimNotes usage dim+              unifyError usage notes bcs $+                "Cannot unify size variable" <+> pquote (ppr dim') <+>+                "with" <+> pquote (pprName vn) <+> "(scope violation)." </>+                "This is because" <+> pquote (ppr dim') <+>+                "is rigidly bound in a deeper scope."+            _ -> modifyConstraints $ M.insert (qualLeaf dim') (lvl, c)+    _ -> return ()++  modifyConstraints $ M.insert vn (lvl, Size (Just dim) usage)+ removeUniqueness :: TypeBase dim as -> TypeBase dim as removeUniqueness (Scalar (Record ets)) =   Scalar $ Record $ fmap removeUniqueness ets@@ -330,23 +636,24 @@   Scalar $ Sum $ (fmap . fmap) removeUniqueness cs removeUniqueness t = t `setUniqueness` Nonunique -mustBeOneOf :: MonadUnify m => [PrimType] -> Usage -> TypeBase () () -> m ()-mustBeOneOf [req_t] loc t = unify loc (Scalar (Prim req_t)) t-mustBeOneOf ts loc t = do+mustBeOneOf :: MonadUnify m => [PrimType] -> Usage -> StructType -> m ()+mustBeOneOf [req_t] usage t = unify usage (Scalar (Prim req_t)) t+mustBeOneOf ts usage t = do+  t' <- normType t   constraints <- getConstraints-  let t' = applySubst (`lookupSubst` constraints) t-      isRigid' v = isRigid v constraints+  let isRigid' v = isRigid v constraints    case t' of     Scalar (TypeVar _ _ (TypeName [] v) [])-      | not $ isRigid' v -> linkVarToTypes loc v ts+      | not $ isRigid' v -> linkVarToTypes usage v ts      Scalar (Prim pt) | pt `elem` ts -> return ()      _ -> failure -  where failure = typeError loc $ "Cannot unify type \"" ++ pretty t ++-                  "\" with any of " ++ intercalate "," (map pretty ts) ++ "."+  where failure = unifyError usage mempty noBreadCrumbs $+                  text "Cannot unify type" <+> pquote (ppr t) <+>+                  "with any of " <> commasep (map ppr ts) <> "."  linkVarToTypes :: MonadUnify m => Usage -> VName -> [PrimType] -> m () linkVarToTypes usage vn ts = do@@ -354,31 +661,34 @@   case vn_constraint of     Just (lvl, Overloaded vn_ts vn_usage) ->       case ts `intersect` vn_ts of-        [] -> typeError usage $ "Type constrained to one of " ++-              intercalate "," (map pretty ts) ++ " but also one of " ++-              intercalate "," (map pretty vn_ts) ++ " due to " ++ show vn_usage ++ "."+        [] -> unifyError usage mempty noBreadCrumbs $+              "Type constrained to one of" <+>+              commasep (map ppr ts) <+> "but also one of" <+>+              commasep (map ppr vn_ts) <+> "due to" <+> ppr vn_usage <> "."         ts' -> modifyConstraints $ M.insert vn (lvl, Overloaded ts' usage)      Just (_, HasConstrs _ vn_usage) ->-      typeError usage $ "Type constrained to one of " ++-      intercalate "," (map pretty ts) ++ ", but also inferred to be sum type due to " ++-      show vn_usage ++ "."+      unifyError usage mempty noBreadCrumbs $+      "Type constrained to one of" <+> commasep (map ppr ts) <>+      ", but also inferred to be sum type due to" <+> ppr vn_usage <> "."      Just (_, HasFields _ vn_usage) ->-      typeError usage $ "Type constrained to one of " ++-      intercalate "," (map pretty ts) ++ ", but also inferred to be record due to " ++-      show vn_usage ++ "."+      unifyError usage mempty noBreadCrumbs $+      "Type constrained to one of" <+> commasep (map ppr ts) <>+      ", but also inferred to be record due to" <+> ppr vn_usage <> "."      Just (lvl, _) -> modifyConstraints $ M.insert vn (lvl, Overloaded ts usage) -    Nothing -> typeError usage $ "Cannot constrain type to one of " ++ intercalate "," (map pretty ts)+    Nothing ->+      unifyError usage mempty noBreadCrumbs $+      "Cannot constrain type to one of" <+> commasep (map ppr ts)  equalityType :: (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>                 Usage -> TypeBase dim as -> m () equalityType usage t = do   unless (orderZero t) $-    typeError usage $-    "Type \"" ++ pretty t ++ "\" does not support equality (is higher-order)."+    unifyError usage mempty noBreadCrumbs $+    "Type " <+> pquote (ppr t) <+> "does not support equality (is higher-order)."   mapM_ mustBeEquality $ typeVars t   where mustBeEquality vn = do           constraints <- getConstraints@@ -387,9 +697,9 @@               mustBeEquality vn'             Just (_, Constraint vn_t cusage)               | not $ orderZero vn_t ->-                  typeError usage $-                  unlines ["Type \"" ++ pretty t ++ "\" does not support equality.",-                           "Constrained to be higher-order due to " ++ show cusage ++ "."]+                  unifyError usage mempty noBreadCrumbs $+                  "Type" <+> pquote (ppr t) <+> "does not support equality." </>+                  "Constrained to be higher-order due to" <+> ppr cusage <+> "."               | otherwise -> return ()             Just (lvl, NoConstraint _ _) ->               modifyConstraints $ M.insert vn (lvl, Equality usage)@@ -400,92 +710,160 @@             Just (_, HasConstrs cs _) ->               mapM_ (equalityType usage) $ concat $ M.elems cs             _ ->-              typeError usage $ "Type " ++ pretty (prettyName vn) ++-              " does not support equality."+              unifyError usage mempty noBreadCrumbs $+              "Type" <+> pprName vn <+> "does not support equality." -zeroOrderType :: (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>-                 Usage -> String -> TypeBase dim as -> m ()-zeroOrderType usage desc t = do+zeroOrderTypeWith :: (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>+                     Usage -> BreadCrumbs -> TypeBase dim as -> m ()+zeroOrderTypeWith usage bcs t = do   unless (orderZero t) $-    typeError usage $ "Type " ++ desc ++-    " must not be functional, but is " ++ quote (pretty t) ++ "."+    unifyError usage mempty bcs $+    "Type" </> indent 2 (ppr t) </> "found to be functional."   mapM_ mustBeZeroOrder . S.toList . typeVars $ t   where mustBeZeroOrder vn = do           constraints <- getConstraints           case M.lookup vn constraints of-            Just (_, Constraint vn_t old_usage)-              | not $ orderZero t ->-                typeError usage $ "Type " ++ desc ++-                " must be non-function, but inferred to be " ++-                quote (pretty vn_t) ++ " due to " ++ show old_usage ++ "."             Just (lvl, NoConstraint _ _) ->               modifyConstraints $ M.insert vn (lvl, NoConstraint Unlifted usage)             Just (_, ParamType Lifted ploc) ->-              typeError usage $ "Type " ++ desc ++-              " must be non-function, but type parameter " ++ quote (prettyName vn) ++ " at " ++-              locStr ploc ++ " may be a function."+              unifyError usage mempty bcs $ "Type parameter" <+>+              pquote (pprName vn) <+> "at" <+>+              text (locStr ploc) <+> "may be a function."             _ -> return () +zeroOrderType :: (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>+                 Usage -> String -> TypeBase dim as -> m ()+zeroOrderType usage desc =+  zeroOrderTypeWith usage $ breadCrumb bc noBreadCrumbs+  where bc = Matching $ "When checking" <+> textwrap desc++unifySharedConstructors :: MonadUnify m =>+                           UnifyDims m -> Usage -> BreadCrumbs+                        -> M.Map Name [StructType]+                        -> M.Map Name [StructType]+                        -> m ()+unifySharedConstructors onDims usage bcs cs1 cs2 =+  forM_ (M.toList $ M.intersectionWith (,) cs1 cs2) $ \(c, (f1, f2)) ->+  unifyConstructor c f1 f2+  where unifyConstructor c f1 f2+          | length f1 == length f2 = do+              let bcs' = breadCrumb (MatchingConstructor c) bcs+              zipWithM_ (unifyWith onDims usage bcs') f1 f2+          | otherwise =+              unifyError usage mempty bcs $+              "Cannot unify constructor" <+> pquote (pprName c) <> "."+ -- | In @mustHaveConstr usage c t fs@, the type @t@ must have a -- constructor named @c@ that takes arguments of types @ts@. mustHaveConstr :: MonadUnify m =>-                  Usage -> Name -> TypeBase dim as -> [TypeBase () ()] -> m ()+                  Usage -> Name -> StructType -> [StructType] -> m () mustHaveConstr usage c t fs = do-  let struct_f = toStructural <$> fs   constraints <- getConstraints   case t of     Scalar (TypeVar _ _ (TypeName _ tn) [])       | Just (lvl, NoConstraint{}) <- M.lookup tn constraints -> do-          mapM_ (scopeCheck usage tn lvl) struct_f-          modifyConstraints $ M.insert tn (lvl, HasConstrs (M.singleton c struct_f) usage)+          mapM_ (scopeCheck usage noBreadCrumbs tn lvl) fs+          modifyConstraints $ M.insert tn (lvl, HasConstrs (M.singleton c fs) usage)       | Just (lvl, HasConstrs cs _) <- M.lookup tn constraints ->-          case M.lookup c cs of-            Nothing  -> modifyConstraints $ M.insert tn (lvl, HasConstrs (M.insert c fs cs) usage)-            Just fs'-              | length fs == length fs' -> zipWithM_ (unify usage) fs fs'-              | otherwise -> typeError usage $ "Different arity for constructor "-                             ++ quote (pretty c) ++ "."+        case M.lookup c cs of+          Nothing  -> modifyConstraints $ M.insert tn (lvl, HasConstrs (M.insert c fs cs) usage)+          Just fs'+            | length fs == length fs' -> zipWithM_ (unify usage) fs fs'+            | otherwise ->+                unifyError usage mempty noBreadCrumbs $+                "Different arity for constructor" <+> pquote (ppr c) <> "."      Scalar (Sum cs) ->       case M.lookup c cs of-        Nothing -> typeError usage $ "Constuctor " ++ quote (pretty c) ++ " not present in type."+        Nothing ->+          unifyError usage mempty noBreadCrumbs $+          "Constuctor" <+> pquote (ppr c) <+> "not present in type."         Just fs'-            | length fs == length fs' -> zipWithM_ (unify usage) fs (toStructural <$> fs')-            | otherwise -> typeError usage $ "Different arity for constructor " ++-                           quote (pretty c) ++ "."+            | length fs == length fs' -> zipWithM_ (unify usage) fs fs'+            | otherwise ->+                unifyError usage mempty noBreadCrumbs $+                "Different arity for constructor" <+> pquote (ppr c) <+> "." -    _ -> do unify usage (toStructural t) $ Scalar $ Sum $ M.singleton c fs+    _ -> do unify usage t $ Scalar $ Sum $ M.singleton c fs             return () -mustHaveField :: (MonadUnify m, Monoid as) =>-                 Usage -> Name -> TypeBase dim as -> m (TypeBase dim as)-mustHaveField usage l t = do+mustHaveFieldWith :: MonadUnify m =>+                     UnifyDims m -> Usage -> BreadCrumbs+                  -> Name -> PatternType -> m PatternType+mustHaveFieldWith onDims usage bcs l t = do   constraints <- getConstraints   l_type <- newTypeVar (srclocOf usage) "t"-  let l_type' = toStructural l_type+  let l_type' = toStruct l_type   case t of     Scalar (TypeVar _ _ (TypeName _ tn) [])       | Just (lvl, NoConstraint{}) <- M.lookup tn constraints -> do-          scopeCheck usage tn lvl l_type'+          scopeCheck usage bcs tn lvl l_type'           modifyConstraints $ M.insert tn (lvl, HasFields (M.singleton l l_type') usage)           return l_type       | Just (lvl, HasFields fields _) <- M.lookup tn constraints -> do           case M.lookup l fields of-            Just t' -> unify usage l_type' t'+            Just t' -> unifyWith onDims usage bcs l_type' t'             Nothing -> modifyConstraints $ M.insert tn                        (lvl, HasFields (M.insert l l_type' fields) usage)           return l_type     Scalar (Record fields)       | Just t' <- M.lookup l fields -> do-          unify usage l_type' (toStructural t')+          unify usage l_type' $ toStruct t'           return t'       | otherwise ->-          typeError usage $-          "Attempt to access field " ++ quote (pretty l) ++ " of value of type " ++-          quote (pretty (toStructural t)) ++ "."-    _ -> do unify usage (toStructural t) $ Scalar $ Record $ M.singleton l l_type'+          unifyError usage mempty bcs $+            "Attempt to access field" <+> pquote (ppr l) <+> " of value of type" <+>+            ppr (toStructural t) <> "."+    _ -> do unify usage (toStruct t) $ Scalar $ Record $ M.singleton l l_type'             return l_type +mustHaveField :: MonadUnify m =>+                 Usage -> Name -> PatternType -> m PatternType+mustHaveField usage = mustHaveFieldWith (unifyDims usage) usage noBreadCrumbs++-- | Replace dimension mismatches with AnyDim.+anyDimOnMismatch :: Monoid as =>+                    TypeBase (DimDecl VName) as -> TypeBase (DimDecl VName) as+                 -> (TypeBase (DimDecl VName) as, [(DimDecl VName, DimDecl VName)])+anyDimOnMismatch t1 t2 = runWriter $ matchDims onDims t1 t2+  where onDims d1 d2+          | d1 == d2 = return d1+          | otherwise = do tell [(d1, d2)]+                           return AnyDim++newDimOnMismatch :: (Monoid as, MonadUnify m) =>+                    SrcLoc -> TypeBase (DimDecl VName) as -> TypeBase (DimDecl VName) as+                 -> m (TypeBase (DimDecl VName) as, [VName])+newDimOnMismatch loc t1 t2 = do+  (t, seen) <- runStateT (matchDims onDims t1 t2) mempty+  return (t, M.elems seen)+  where r = Rigid $ RigidCond (toStruct t1) (toStruct t2)+        onDims d1 d2+          | d1 == d2 = return d1+          | otherwise = do+              -- Remember mismatches we have seen before and reuse the+              -- same new size.+              maybe_d <- gets $ M.lookup (d1, d2)+              case maybe_d of+                Just d -> return $ NamedDim $ qualName d+                Nothing -> do+                  d <- lift $ newDimVar loc r "differ"+                  modify $ M.insert (d1, d2) d+                  return $ NamedDim $ qualName d++-- | Like unification, but creates new size variables where mismatches+-- occur.  Returns the new dimensions thus created.+unifyMostCommon :: MonadUnify m =>+                   Usage -> PatternType -> PatternType -> m (PatternType, [VName])+unifyMostCommon usage t1 t2 = do+  -- We are ignoring the dimensions here, because any mismatches+  -- should be turned into fresh size variables.+  unify usage (toStruct (anySizes t1))+              (toStruct (anySizes t2))+  t1' <- normTypeFully t1+  t2' <- normTypeFully t2+  newDimOnMismatch (srclocOf usage) t1' t2'+ -- Simple MonadUnify implementation.  type UnifyMState = (Constraints, Int)@@ -495,20 +873,41 @@             MonadState UnifyMState,             MonadError TypeError) +newVar :: String -> UnifyM VName+newVar name = do+  (x, i) <- get+  put (x, i+1)+  return $ VName (mkTypeVarName name i) i+ instance MonadUnify UnifyM where   getConstraints = gets fst   putConstraints x = modify $ \(_, i) -> (x, i) -  newTypeVar loc desc = do-    i <- do (x, i) <- get-            put (x, i+1)-            return i-    let v = VName (mkTypeVarName desc i) 0+  newTypeVar loc name = do+    v <- newVar name     modifyConstraints $ M.insert v (0, NoConstraint Lifted $ Usage Nothing loc)     return $ Scalar $ TypeVar mempty Nonunique (typeName v) [] +  newDimVar loc rigidity name = do+    dim <- newVar name+    case rigidity of+      Rigid src -> modifyConstraints $ M.insert dim (0, UnknowableSize loc src)+      Nonrigid -> modifyConstraints $ M.insert dim (0, Size Nothing $ Usage Nothing loc)+    return dim+   curLevel = pure 0 +  unifyError loc notes bcs doc =+    throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs++  matchError loc notes bcs t1 t2 =+    throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs+    where doc = "Types" </>+                indent 2 (ppr t1) </>+                "and" </>+                indent 2 (ppr t2) </>+                "do not match."+ -- | Construct a the name of a new type variable given a base -- description and a tag number (note that this is distinct from -- actually constructing a VName; the tag here is intended for human@@ -518,20 +917,21 @@   nameFromString $ desc ++ mapMaybe subscript (show i)   where subscript = flip lookup $ zip "0123456789" "₀₁₂₃₄₅₆₇₈₉" -instance MonadBreadCrumbs UnifyM where+runUnifyM :: [TypeParam] -> UnifyM a -> Either TypeError a+runUnifyM tparams (UnifyM m) = runExcept $ evalStateT m (constraints, 0)+  where constraints = M.fromList $ map f tparams+        f (TypeParamDim p loc) = (p, (0, Size Nothing $ Usage Nothing loc))+        f (TypeParamType l p loc) = (p, (0, NoConstraint l $ Usage Nothing loc))  -- | Perform a unification of two types outside a monadic context.--- The type parameters are allowed to be instantiated (with--- 'TypeParamDim ignored); all other types are considered rigid.+-- The type parameters are allowed to be instantiated; all other types+-- are considered rigid. doUnification :: SrcLoc -> [TypeParam]-              -> TypeBase () () -> TypeBase () ()-              -> Either TypeError (TypeBase () ())+              -> StructType -> StructType+              -> Either TypeError StructType doUnification loc tparams t1 t2 = runUnifyM tparams $ do-  unify (Usage Nothing loc) t1 t2-  normaliseType t2--runUnifyM :: [TypeParam] -> UnifyM a -> Either TypeError a-runUnifyM tparams (UnifyM m) = runExcept $ evalStateT m (constraints, 0)-  where constraints = M.fromList $ mapMaybe f tparams-        f TypeParamDim{} = Nothing-        f (TypeParamType l p loc) = Just (p, (0, NoConstraint l $ Usage Nothing loc))+  let rsrc = RigidUnify+  (t1', _) <- instantiateEmptyArrayDims loc "n" (Rigid rsrc) t1+  (t2', _) <- instantiateEmptyArrayDims loc "m" (Rigid rsrc) t2+  expect (Usage Nothing loc) t1' t2'+  normTypeFully t2
src/futhark.hs view
@@ -27,6 +27,7 @@ import qualified Futhark.CLI.CSOpenCL as CSOpenCL import qualified Futhark.CLI.Test as Test import qualified Futhark.CLI.Bench as Bench+import qualified Futhark.CLI.Check as Check import qualified Futhark.CLI.Dataset as Dataset import qualified Futhark.CLI.Datacmp as Datacmp import qualified Futhark.CLI.Pkg as Pkg@@ -66,7 +67,7 @@            , ("doc", (Doc.main, "Generate documentation for Futhark code."))            , ("pkg", (Pkg.main, "Manage local packages.")) -           , ("check", (Misc.mainCheck, "Type check a program."))+           , ("check", (Check.main, "Type check a program."))            , ("imports", (Misc.mainImports, "Print all non-builtin imported Futhark files."))            , ("autotune", (Autotune.main, "Autotune threshold parameters."))            , ("query", (Query.main, "Query semantic information about program."))