packages feed

futhark 0.24.2 → 0.24.3

raw patch · 25 files changed

+425/−158 lines, 25 filesdep ~lspdep ~versionsPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: lsp, versions

API changes (from Hackage documentation)

+ Futhark.CodeGen.Backends.GenericC.Code: compileArg :: Arg -> CompilerM op s Exp
+ Futhark.CodeGen.Backends.GenericC.Code: compileDest :: VName -> CompilerM op s (VName, [Stm])
+ Language.Futhark.Syntax: TEParens :: TypeExp f vn -> SrcLoc -> TypeExp f vn

Files

docs/glossary.rst view
@@ -9,6 +9,11 @@ .. glossary::    :sorted: +   Abstract type++     A type whose definition has been hidden through a :term:`module+     ascription`.+    Aliases       The *aliases* of a variable is a set of those other variables@@ -91,6 +96,22 @@      Futhark. `See also Wikipedia      <https://en.wikipedia.org/wiki/Data_parallelism>`_. +   Defunctionalisation++     A program transformation always performed by the Futhark compiler,+     that replaces function values with non-function values.  The goal+     is to avoid having indirect calls through function pointers at+     run-time.  To permit zero-overhead defunctionalisation, the+     Futhark type rules impose restrictions on :term:`lifted types+     <lifted type>`.++   Defunctorisation++     A program transformation always performed by the Futhark+     compiler, that compiles away modules using an approach similar to+     :term:`defunctionalisation`.  This makes using e.g. a+     :term:`parametric module` completely free at run-time.+    Elaboration       The process conducted out by the type checker, where it infers@@ -118,6 +139,13 @@      more restrictions than some other backends, particularly with      respect to :term:`irregular nested data parallelism`. +   Higher-ranked type++     A type that does not describe :term:`values <value>`.  Can be+     seen as a partially applied :term:`type constructor`.  Not+     directly supported by Futhark, but a similar effect can be+     achieved through the :ref:`module-system`.+    In-place updates       A somewhat misleading term for the syntactic forms ``x with [i] =@@ -125,6 +153,10 @@      updates, but can be operationally understood as thus.  See      :ref:`in-place-updates`. +   Invariant++     Not :term:`variant`.+    Irregular       Something that is not regular.  Usually used as shorthand for@@ -140,9 +172,9 @@    Irregular nested data parallelism       An instance of :term:`nested data parallelism`, where the-     :term:`parallel width` of inner parallelism is term:`variant` to-     the outer parallelism.  For example, the expression following-     expression exhibits irregular nested data parallelism::+     :term:`parallel width` of inner parallelism is :term:`variant` to+     the outer parallelism.  For example, the following expression+     exhibits irregular nested data parallelism::         map (\n -> reduce (+) 0 (iota n)) ns @@ -177,16 +209,64 @@      A function from :term:`modules<module>` to modules.  The most      powerful form of abstraction provided by Futhark. +   Polymorphic++     Usually means a :term:`polymorphic function`, but sometimes a+     :term:`parametric modules <parametric module>`.  Should not be+     used to describe a :term:`type constructor <type constructor>`.++   Polymorphism++     The concept of being :term:`polymorphic`.++   Polymorphic function++     A function with :term:`type parameters <type parameter>`, such+     that the function can be applied to arguments of various types.+     Compiled using :term:`monomorphisation`.+    Lifted type -     A type that may contain functions.  These have various-     restrictions on their use.  See :ref:`hofs`.+     A type that may contain functions, including function types+     themselves.  These have various restrictions on their use in+     order to support :term:`defunctionalisation`.  See :ref:`hofs`.     Module       A mapping from names to definitions of types, values, or nested      modules.  See :ref:`module-system`. +   Module ascription++     A feature of the module system through which the contents of a+     module can be hidden.  Written as ``m : mt`` where ``m`` is a+     :term:`module expression` and ``mt`` is a :term:`module type+     expression`.  See :ref:`module-system`.++   Module expression++     An expression that is evaluated at compile time, through+     :term:`defunctorisation` to a :term:`module`.  Most commonly just+     the name of a module.++   Module type++     A description of the interface of a :term:`module`.  Most commonly+     used to hide contents in a :term:`module ascription` or to+     require implementation of an interface in a :term:`parametric+     module`.++   Module type expression++     An expression that is evaluated during type-checking to a+     :term:`module type`.++   Monomorphisation++     A program transformation that instantiates a copy of each+     :term:`polymorphic` functions for each type it is used with.+     Performed by the Futhark compiler.+    Nested data parallelism       Nested :term:`data parallelism` occurs when a parallel construct@@ -202,11 +282,20 @@      fully exploit the parallelism of the program, although      :term:`nested data parallelism` muddles the picture. +   Recursion++     A function that calls itself.  Currently not supported in+     Futhark.+    Regular nested data parallelism       An instance of :term:`nested data parallelism` that is not      :term:`irregular`.  Fully supports by any :term:`GPU backend`. +   Size++     The symbolic size of an array dimension or :term:`abstract type`.+    Size types    Size-dependent types @@ -219,6 +308,21 @@      array elements, as that might potentially result in an      :term:`irregular array`.  See :ref:`typeabbrevs`. +   Size argument++     An argument to a :term:`type constructor` in a :term:`type+     expression` of the form ``[n]`` or ``[]``.  The latter is called+     an :term:`anonymous size`.  Must match a corresponding+     :term:`size parameter`.++   Size parameter++     A parameter of a :term:`polymorphic function` or :term:`type+     constructor` that ranges over :term:`sizes <size>`.  These are+     written as `[n]` for some `n`, after which `n` is in scope as a+     term of type ``i64`` within the rest of the definition.  Do not+     confuse them with :term:`type parameters <type parameter>`.+    SOAC    Second Order Array Combinator @@ -227,6 +331,48 @@      on.  They are *second order* because they accept a functional      argument, and so permit :term:`nested data parallelism`. +   Type++     A classification of values.  ``i32`` and ``[10]i32`` are examples+     of types.++   Type abbreviation++     A shorthand for a longer type, e.g. ``type t = [100]i32``.  Can+     accept :term:`type parameters <type parameter>` and :term:`size+     parameters <type parameter>`.  The definition is visible to+     users, unless hidden with a :term:`module ascription`.  See+     :ref:`typeabbrevs`.++   Type argument++     An argument to a :term:`type constructor` that is itself a+     :term:`type`.  Must match a corresponding :term:`type parameter`.++   Type constructor++     A :term:`type abbreviation` or :term:`abstract type` that has at+     least one :term:`type parameter` or :term:`size parameter`.+     Futhark does not support :term:`higher-ranked types+     <higher-ranked type>`, so when referencing a type constructor in+     a :term:`type expression`, you must provide corresponding+     :term:`type arguments <type argument>` and :term:`size arguments+     <size argument>` in an appopriate order.++   Type expression++     A syntactic construct that is evaluated to a :term:`type` in the+     type checker, but may contain uses of :term:`type abbreviations+     <type abbreviation>` and :term:`anonymous sizes <anonymous size>`.++   Type parameter++     A parameter of a :term:`polymorphic function` or :term:`type+     constructor` that ranges over types.  These are written as `'t`+     for some `t`, after which `t` is in scope as a type within the+     rest of the definition.  Do not confuse them with :term:`size+     parameters <size parameter>`.+    Uniqueness types       A somewhat misleading term that describes Futhark's system of@@ -240,3 +386,19 @@       A size produced by invoking a function whose result type contains      an existentially quantified size, such as ``filter``.++   Value++     An object such as the integer ``123`` or the array ``[1,2,3]``.+     Expressions variables are bound to values and all valid+     expressions have a :term:`type` describing the form of values+     they can return.++   Variant++     When some value ``v`` computed inside a loop takes a different+     value for each iteration inside the loop, we say that ``v`` is+     *variant* to the loop (and otherwise :term:`invariant`).  Often+     used to talk about :term:`irregularity <irregular>`.  When+     something is nested inside multiple loops, it may be variant to+     just one of them.
docs/versus-other-languages.rst view
@@ -43,7 +43,7 @@  Identifiers are case-sensitive, and there is no restriction on the case of the first letter (unlike Haskell and OCaml, but like Standard-ML).+ML and Flix).  User-defined operators are possible, but the fixity of the operator depends on its name.  Specifically, the fixity of a user-defined@@ -52,14 +52,14 @@ 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 ``def``.  Local-variables are bound with ``let``.+Top-level functions and values are defined with ``def`` as in Flix.+Local variables are bound with ``let``.  Evaluation ---------- -Futhark is a completely pure language, with no cheating through monads-or anything of the sort.+Futhark is a completely pure language, with no cheating through+monads, effect systems, or anything of the sort.  Evaluation is *eager* or *call-by-value*, like most non-Haskell languages.  However, there is no defined evaluation order.@@ -72,15 +72,15 @@  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.+impossible. The Futhark compiler does not automatically go looking for+parallelism.  Only certain special constructs and built-in library+functions (such as ``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 ``assert``.+Currying and partial application work as usual (although functions are+not fully first class; see `Types`_).  Although the ``assert``+construct looks like a function, it is not, and it cannot be partially+applied.  Lambda terms are written as ``\x -> x + 2``, as in Haskell. @@ -109,9 +109,10 @@ ``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`.+*structurally typed records*, as in Standard ML and Flix.+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
futhark.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:           futhark-version:        0.24.2+version:        0.24.3 synopsis:       An optimising compiler for a functional, array-oriented language.  description:    Futhark is a small programming language designed to be compiled to
src/Futhark/CLI/Autotune.hs view
@@ -471,6 +471,9 @@    T.putStrLn $ "Result of autotuning:\n" <> tuning +supportedBackends :: [String]+supportedBackends = ["opencl", "cuda"]+ commandLineOptions :: [FunOptDescr AutotuneOptions] commandLineOptions =   [ Option@@ -480,12 +483,9 @@           ( \n ->               case reads n of                 [(n', "")] | n' >= 0 ->-                  Right $ \config ->-                    config-                      { optMinRuns = n'-                      }+                  Right $ \config -> config {optMinRuns = n'}                 _ ->-                  Left $ error $ "'" ++ n ++ "' is not a non-negative integer."+                  Left $ optionsError $ "'" ++ n ++ "' is not a non-negative integer."           )           "RUNS"       )@@ -494,10 +494,14 @@       []       ["backend"]       ( ReqArg-          (\backend -> Right $ \config -> config {optBackend = backend})+          ( \backend ->+              if backend `elem` supportedBackends+                then Right $ \config -> config {optBackend = backend}+                else Left $ optionsError $ "autotuning is only supported for these backends: " <> unwords supportedBackends+          )           "BACKEND"       )-      "The compiler used (defaults to 'opencl').",+      "The backend used (defaults to 'opencl').",     Option       []       ["futhark"]@@ -534,7 +538,7 @@                 [(n', "")] ->                   Right $ \config -> config {optTimeout = n'}                 _ ->-                  Left $ error $ "'" ++ n ++ "' is not a non-negative integer."+                  Left $ optionsError $ "'" ++ n ++ "' is not a non-negative integer."           )           "SECONDS"       )
src/Futhark/CLI/Dataset.hs view
@@ -192,6 +192,7 @@ toValueType TEArrow {} = Left "Cannot generate functions." toValueType TESum {} = Left "Cannot handle sumtypes yet." toValueType TEDim {} = Left "Cannot handle existential sizes."+toValueType (TEParens t _) = toValueType t toValueType (TEUnique t _) = toValueType t toValueType (TEArray d t _) = do   d' <- constantDim d
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -495,8 +495,9 @@               $ M.keys params       earlyDecl         [C.cedecl|static void set_tuning_params(struct futhark_context* ctx) {-                $stms:set_tuning_params-              }|]+                    (void)ctx;+                    $stms:set_tuning_params+                  }|]        mapM_ earlyDecl $ concat memfuns       type_funs <- generateAPITypes arr_space types@@ -540,10 +541,10 @@       num_params = length params   earlyDecl [C.cedecl|struct tuning_params { $sdecls:size_decls };|]   earlyDecl [C.cedecl|static const int num_tuning_params = $int:num_params;|]-  earlyDecl [C.cedecl|static const char *tuning_param_names[] = { $inits:size_name_inits };|]-  earlyDecl [C.cedecl|static const char *tuning_param_vars[] = { $inits:size_var_inits };|]-  earlyDecl [C.cedecl|static const char *tuning_param_classes[] = { $inits:size_class_inits };|]-  earlyDecl [C.cedecl|static typename int64_t tuning_param_defaults[] = { $inits:size_default_inits };|]+  earlyDecl [C.cedecl|static const char *tuning_param_names[] = { $inits:size_name_inits, NULL };|]+  earlyDecl [C.cedecl|static const char *tuning_param_vars[] = { $inits:size_var_inits, NULL };|]+  earlyDecl [C.cedecl|static const char *tuning_param_classes[] = { $inits:size_class_inits, NULL };|]+  earlyDecl [C.cedecl|static typename int64_t tuning_param_defaults[] = { $inits:size_default_inits, 0 };|]  generateCommonLibFuns :: [C.BlockItem] -> CompilerM op s () generateCommonLibFuns memreport = do
src/Futhark/CodeGen/Backends/GenericC/Code.hs view
@@ -6,6 +6,8 @@     compileExp,     compileExpToName,     compileCode,+    compileDest,+    compileArg,     errorMsgString,     linearCode,   )@@ -158,10 +160,36 @@   iexp' <- compileExp $ untyped iexp   pure [C.cexp|$id:src[$exp:iexp']|] +memNeedsWrapping :: VName -> CompilerM op s Bool+memNeedsWrapping v = do+  refcount <- fatMemory DefaultSpace+  cached <- isJust <$> cacheMem v+  pure $ refcount && cached++-- | Compile an argument to a function applicaiton. compileArg :: Arg -> CompilerM op s C.Exp-compileArg (MemArg m) = pure [C.cexp|$exp:m|]+compileArg (MemArg m) = do+  -- Function might expect fat memory, so if this is a lexical/cached+  -- raw pointer, we have to wrap it in a struct.+  wrap <- memNeedsWrapping m+  if wrap+    then pure [C.cexp|($ty:(fatMemType DefaultSpace)) {.references = NULL, .mem = $exp:m}|]+    else pure [C.cexp|$exp:m|] compileArg (ExpArg e) = compileExp e +-- | Prepare a destination for function application.+compileDest :: VName -> CompilerM op s (VName, [C.Stm])+compileDest v = do+  -- Function result be fat memory, so if target is a raw pointer, we+  -- have to wrap it in a struct and unwrap it afterwards.+  wrap <- memNeedsWrapping v+  if wrap+    then do+      v' <- newVName $ baseString v <> "_struct"+      item [C.citem|$ty:(fatMemType DefaultSpace) $id:v' = {.references = NULL, .mem = $exp:v};|]+      pure (v', [C.cstms|$id:v = $id:v'.mem;|])+    else pure (v, mempty)+ compileCode :: Code op -> CompilerM op s () compileCode (Op op) =   join $ asks (opsCompiler . envOperations) <*> pure op@@ -358,9 +386,11 @@   | isBuiltInFunction fname = do       args' <- mapM compileArg args       stm [C.cstm|$id:dest = $id:(funName fname)($args:args');|]-compileCode (Call dests fname args) =+compileCode (Call dests fname args) = do+  (dests', unpack_dest) <- mapAndUnzipM compileDest dests   join $     asks (opsCall . envOperations)-      <*> pure dests+      <*> pure dests'       <*> pure fname       <*> mapM compileArg args+  stms $ mconcat unpack_dest
src/Futhark/CodeGen/Backends/GenericC/Monad.hs view
@@ -608,7 +608,8 @@   -- cached.  This is not a deep technical restriction, but merely a   -- heuristic based on GPU memory usually involving larger   -- allocations, that do not suffer from the overhead of reference-  -- counting.+  -- counting.  Beware: there is code elsewhere in codegen that+  -- assumes lexical memory is DefaultSpace too.   let cached = M.keys $ M.filter (== DefaultSpace) lexical    cached' <- forM cached $ \mem -> do
src/Futhark/CodeGen/Backends/MulticoreC.hs view
@@ -477,9 +477,7 @@   ftask <- multicoreDef (s' ++ "_parloop") $ \s -> do     fbody <- benchmarkCode s (Just "tid") <=< GC.inNewFunction $       GC.cachingMemory lexical $ \decl_cached free_cached -> GC.collect $ do-        mapM_-          GC.item-          [C.citems|$decls:(compileGetStructVals fstruct free_args free_ctypes)|]+        GC.items [C.citems|$decls:(compileGetStructVals fstruct free_args free_ctypes)|]          GC.decl [C.cdecl|typename int64_t iterations = end-start;|] @@ -496,6 +494,7 @@                                  typename int64_t end,                                  int subtask_id,                                  int tid) {+                       (void)subtask_id;                        int err = 0;                        struct $id:fstruct *$id:fstruct = (struct $id:fstruct*) args;                        struct futhark_context *ctx = $id:fstruct->ctx;
src/Futhark/CodeGen/Backends/MulticoreISPC.hs view
@@ -630,11 +630,11 @@       [C.cstm|if (!($exp:cond')) { $items:fbranch' }|]     _ ->       [C.cstm|if ($exp:cond') { $items:tbranch' } else { $items:fbranch' }|]-compileCode (Call dests fname args) =-  defCallIspc dests fname =<< mapM compileArg args+compileCode (Call dests fname args) = do+  (dests', unpack_dest) <- mapAndUnzipM GC.compileDest dests+  defCallIspc dests' fname =<< mapM GC.compileArg args+  GC.stms $ mconcat unpack_dest   where-    compileArg (MemArg m) = pure [C.cexp|$exp:m|]-    compileArg (ExpArg e) = compileExp e     defCallIspc dests' fname' args' = do       let out_args = [[C.cexp|&$id:d|] | d <- dests']           args''
src/Futhark/CodeGen/ImpGen/GPU/SegRed.hs view
@@ -83,6 +83,7 @@   KernelBody GPUMem ->   CallKernelGen () compileSegRed pat lvl space reds body = do+  emit $ Imp.DebugPrint "\n# SegRed" Nothing   KernelAttrs _ _ num_groups group_size <- lvlKernelAttrs lvl   let grid = KernelGrid num_groups group_size   compileSegRed' pat grid space reds $ \red_cont ->@@ -94,6 +95,7 @@         zipWithM_ (compileThreadResult space) map_arrs map_res        red_cont $ zip (map kernelResultSubExp red_res) $ repeat []+  emit $ Imp.DebugPrint "" Nothing  -- | Like 'compileSegRed', but where the body is a monadic action. compileSegRed' ::@@ -197,8 +199,6 @@     dPrimV "num_threads" $       unCount num_groups' * unCount group_size' -  emit $ Imp.DebugPrint "\n# SegRed" Nothing-   sKernelThread "segred_nonseg" (segFlat space) (defKernelAttrs num_groups group_size) $ do     constants <- kernelConstants <$> askEnv     sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "local"@@ -253,8 +253,6 @@           group_res_arrs           red_arrs -  emit $ Imp.DebugPrint "" Nothing- smallSegmentsReduction ::   Pat LetDecMem ->   Count NumGroups SubExp ->@@ -279,7 +277,7 @@       segments_per_group = unCount group_size' `quot` segment_size_nonzero       required_groups = sExt32 $ num_segments `divUp` segments_per_group -  emit $ Imp.DebugPrint "\n# SegRed-small" Nothing+  emit $ Imp.DebugPrint "# SegRed-small" Nothing   emit $ Imp.DebugPrint "num_segments" $ Just $ untyped num_segments   emit $ Imp.DebugPrint "segment_size" $ Just $ untyped segment_size   emit $ Imp.DebugPrint "segments_per_group" $ Just $ untyped segments_per_group@@ -370,8 +368,6 @@       -- local memory array first thing in the next iteration.       sOp $ Imp.Barrier Imp.FenceLocal -  emit $ Imp.DebugPrint "" Nothing- largeSegmentsReduction ::   Pat LetDecMem ->   Count NumGroups SubExp ->@@ -406,7 +402,7 @@     dPrimV "threads_per_segment" $       groups_per_segment * unCount group_size' -  emit $ Imp.DebugPrint "\n# SegRed-large" Nothing+  emit $ Imp.DebugPrint "# SegRed-large" Nothing   emit $ Imp.DebugPrint "num_segments" $ Just $ untyped num_segments   emit $ Imp.DebugPrint "segment_size" $ Just $ untyped segment_size   emit $ Imp.DebugPrint "virt_num_groups" $ Just $ untyped $ tvExp virt_num_groups@@ -508,8 +504,6 @@                     copyDWIMFix (patElemName v) (map Imp.le64 segment_gtids) (Var acc) acc_is        sIf (groups_per_segment .==. 1) one_group_per_segment multiple_groups_per_segment--  emit $ Imp.DebugPrint "" Nothing  -- Careful to avoid division by zero here.  We have at least one group -- per segment.
src/Futhark/CodeGen/ImpGen/GPU/SegScan.hs view
@@ -63,5 +63,6 @@       | Just scan' <- canBeSinglePass scans ->           SinglePass.compileSegScan pat lvl space scan' kbody     _ -> TwoPass.compileSegScan pat lvl space scans kbody+  emit $ Imp.DebugPrint "" Nothing   where     n = product $ map pe64 $ segSpaceDims space
src/Futhark/Doc/Generator.hs view
@@ -641,6 +641,7 @@         t' <- typeExpHtml t         pure $ toHtml (nameToString name) <> ": " <> t'   TEVar name _ -> qualNameHtml name+  TEParens te _ -> parens <$> typeExpHtml te   TEApply t arg _ -> do     t' <- typeExpHtml t     arg' <- typeArgExpHtml arg
src/Futhark/IR/Pretty.hs view
@@ -170,19 +170,10 @@     align . hang 2 $       "let"         <+> align (pretty pat)-        <+> case (linebreak, stmannot) of-          (True, []) -> equals </> pretty e-          (False, []) -> equals <+> pretty e-          (_, ann) -> equals </> (stack ann </> pretty e)+        <+> case stmannot of+          [] -> equals </> pretty e+          _ -> equals </> (stack stmannot </> pretty e)     where-      linebreak = case e of-        BasicOp BinOp {} -> False-        BasicOp CmpOp {} -> False-        BasicOp ConvOp {} -> False-        BasicOp UnOp {} -> False-        BasicOp SubExp {} -> False-        _ -> True-       stmannot =         concat           [ maybeToList (ppExpDec (stmAuxDec aux) e),
src/Futhark/Internalise/Defunctionalise.hs view
@@ -169,6 +169,8 @@       TESum (map (fmap $ map $ onTypeExp substs) ts) loc     onTypeExp substs (TEDim dims t loc) =       TEDim dims (onTypeExp substs t) loc+    onTypeExp substs (TEParens te loc) =+      TEParens (onTypeExp substs te) loc     onTypeExp _ (TEVar v loc) =       TEVar v loc @@ -343,7 +345,9 @@   StaticVal ->   m StaticVal instStaticVal globals dims t sv_t sv = do-  fresh_substs <- mkSubsts $ S.toList $ S.fromList dims <> sizesToRename sv+  fresh_substs <-+    mkSubsts . filter (`S.notMember` globals) . S.toList $+      S.fromList dims <> sizesToRename sv    let dims' = map (onName fresh_substs) dims       isDim k _ = k `elem` dims'@@ -494,8 +498,7 @@     HoleSV _ hole_loc ->       pure (Hole (Info t) hole_loc, sv)     _ ->-      let tp = typeFromSV sv-       in pure (Var qn (Info tp) loc, sv)+      pure (Var qn (Info (typeFromSV sv)) loc, sv) defuncExp (Hole (Info t) loc) =   pure (Hole (Info t) loc, HoleSV t loc) defuncExp (Ascript e0 tydecl loc)@@ -510,7 +513,7 @@   | otherwise = defuncExp e0 defuncExp (AppExp (LetPat sizes pat e1 e2 loc) (Info (AppRes t retext))) = do   (e1', sv1) <- defuncExp e1-  let env = matchPatSV pat sv1+  let env = alwaysMatchPatSV pat sv1       pat' = updatePat pat sv1   (e2', sv2) <- localEnv env $ defuncExp e2   -- To maintain any sizes going out of scope, we need to compute the@@ -547,7 +550,7 @@ defuncExp IndexSection {} = error "defuncExp: unexpected projection section." defuncExp (AppExp (DoLoop sparams pat e1 form e3 loc) res) = do   (e1', sv1) <- defuncExp e1-  let env1 = matchPatSV pat sv1+  let env1 = alwaysMatchPatSV pat sv1   (form', env2) <- case form of     For v e2 -> do       e2' <- defuncExp' e2@@ -655,7 +658,10 @@       ++ locStr loc defuncExp (AppExp (Match e cs loc) res) = do   (e', sv) <- defuncExp e-  csPairs <- mapM (defuncCase sv) cs+  let bad = error $ "No case matches StaticVal\n" <> show sv+  csPairs <-+    fromMaybe bad . NE.nonEmpty . catMaybes+      <$> mapM (defuncCase sv) (NE.toList cs)   let cs' = fmap fst csPairs       sv' = snd $ NE.head csPairs   pure (AppExp (Match e' cs' loc) res, sv')@@ -667,12 +673,15 @@ defuncExp' :: Exp -> DefM Exp defuncExp' = fmap fst . defuncExp -defuncCase :: StaticVal -> Case -> DefM (Case, StaticVal)+defuncCase :: StaticVal -> Case -> DefM (Maybe (Case, StaticVal)) defuncCase sv (CasePat p e loc) = do   let p' = updatePat p sv-      env = matchPatSV p sv-  (e', sv') <- localEnv env $ defuncExp e-  pure (CasePat p' e' loc, sv')+  case matchPatSV p sv of+    Just env -> do+      (e', sv') <- localEnv env $ defuncExp e+      pure $ Just (CasePat p' e' loc, sv')+    Nothing ->+      pure Nothing  -- | Defunctionalize the function argument to a SOAC by eta-expanding if -- necessary and then defunctionalizing the body of the introduced lambda.@@ -862,7 +871,7 @@   DefM (Exp, StaticVal) defuncApplyArg fname_s (f', f_sv@(LambdaSV pat lam_e_t lam_e closure_env)) (((d, argext), arg), _) = do   (arg', arg_sv) <- defuncExp arg-  let env' = matchPatSV pat arg_sv+  let env' = alwaysMatchPatSV pat arg_sv       dims = mempty   (lam_e', sv) <-     localNewEnv (env' <> closure_env) $@@ -929,8 +938,12 @@       apply_e = mkApply f' [(d, argext, arg')] callret   pure (apply_e, sv) ---defuncApplyArg _ (_, sv) _ =-  error $ "defuncApplyArg: cannot apply StaticVal\n" <> show sv+defuncApplyArg fname_s (_, sv) _ =+  error $+    "defuncApplyArg: cannot apply StaticVal\n"+      <> show sv+      <> "\nFunction name: "+      <> prettyString fname_s  updateReturn :: AppRes -> Exp -> Exp updateReturn (AppRes ret1 ext1) (AppExp apply (Info (AppRes ret2 ext2))) =@@ -1119,43 +1132,49 @@    in (second fromStruct p : ps', ret) dynamicFunType sv _ = ([], typeFromSV sv) --- | Match a pattern with its static value. Returns an environment with--- the identifier components of the pattern mapped to the corresponding--- subcomponents of the static value.-matchPatSV :: PatBase Info VName -> StaticVal -> Env+-- | Match a pattern with its static value. Returns an environment+-- with the identifier components of the pattern mapped to the+-- corresponding subcomponents of the static value.  If this function+-- returns 'Nothing', then it corresponds to an unmatchable case.+-- These should only occur for 'Match' expressions.+matchPatSV :: Pat -> StaticVal -> Maybe Env matchPatSV (TuplePat ps _) (RecordSV ls) =-  mconcat $ zipWith (\p (_, sv) -> matchPatSV p sv) ps ls+  mconcat <$> zipWithM (\p (_, sv) -> matchPatSV p sv) ps ls matchPatSV (RecordPat ps _) (RecordSV ls)   | ps' <- sortOn fst ps,     ls' <- sortOn fst ls,     map fst ps' == map fst ls' =-      mconcat $ zipWith (\(_, p) (_, sv) -> matchPatSV p sv) ps' ls'+      mconcat <$> zipWithM (\(_, p) (_, sv) -> matchPatSV p sv) ps' ls' matchPatSV (PatParens pat _) sv = matchPatSV pat sv matchPatSV (PatAttr _ pat _) sv = matchPatSV pat sv matchPatSV (Id vn (Info t) _) sv =-  -- When matching a pattern with a zero-order STaticVal, the type of-  -- the pattern wins out.  This is important when matching a-  -- nonunique pattern with a unique value.-  if orderZeroSV sv-    then dim_env <> M.singleton vn (Binding Nothing $ Dynamic t)-    else dim_env <> M.singleton vn (Binding Nothing sv)+  -- When matching a zero-order pattern with a StaticVal, the type of+  -- the pattern wins out.  This is important for propagating sizes+  -- (but probably reveals a flaw in our bookkeeping).+  pure $+    if orderZero t+      then dim_env <> M.singleton vn (Binding Nothing $ Dynamic t)+      else dim_env <> M.singleton vn (Binding Nothing sv)   where     dim_env =       M.fromList $ map (,i64) $ S.toList $ freeInType t     i64 = Binding Nothing $ Dynamic $ Scalar $ Prim $ Signed Int64-matchPatSV (Wildcard _ _) _ = mempty+matchPatSV (Wildcard _ _) _ = pure mempty matchPatSV (PatAscription pat _ _) sv = matchPatSV pat sv-matchPatSV PatLit {} _ = mempty+matchPatSV PatLit {} _ = pure mempty matchPatSV (PatConstr c1 _ ps _) (SumSV c2 ls fs)   | c1 == c2 =-      mconcat $ zipWith matchPatSV ps ls-  | Just ts <- lookup c1 fs =-      mconcat $ zipWith matchPatSV ps $ map svFromType ts+      mconcat <$> zipWithM matchPatSV ps ls+  | Just _ <- lookup c1 fs =+      Nothing   | otherwise =       error $ "matchPatSV: missing constructor in type: " ++ prettyString c1 matchPatSV (PatConstr c1 _ ps _) (Dynamic (Scalar (Sum fs)))   | Just ts <- M.lookup c1 fs =-      mconcat $ zipWith matchPatSV ps $ map svFromType ts+      -- A higher-order pattern can only match an appropriate SumSV.+      if all orderZero ts+        then mconcat <$> zipWithM matchPatSV ps (map svFromType ts)+        else Nothing   | otherwise =       error $ "matchPatSV: missing constructor in type: " ++ prettyString c1 matchPatSV pat (Dynamic t) = matchPatSV pat $ svFromType t@@ -1167,10 +1186,10 @@       ++ "\n with static value\n"       ++ show sv -orderZeroSV :: StaticVal -> Bool-orderZeroSV Dynamic {} = True-orderZeroSV (RecordSV fields) = all (orderZeroSV . snd) fields-orderZeroSV _ = False+alwaysMatchPatSV :: Pat -> StaticVal -> Env+alwaysMatchPatSV pat sv = fromMaybe bad $ matchPatSV pat sv+  where+    bad = error $ unlines [prettyString pat, "cannot match StaticVal", show sv]  -- | Given a pattern and the static value for the defunctionalized argument, -- update the pattern to reflect the changes in the types.@@ -1220,8 +1239,9 @@       ++ "\nto reflect the static value\n"       ++ show sv --- | Convert a record (or tuple) type to a record static value. This is used for--- "unwrapping" tuples and records that are nested in 'Dynamic' static values.+-- | Convert a record (or tuple) type to a record static value. This+-- is used for "unwrapping" tuples and records that are nested in+-- 'Dynamic' static values. svFromType :: PatType -> StaticVal svFromType (Scalar (Record fs)) = RecordSV . M.toList $ M.map svFromType fs svFromType t = Dynamic t
src/Futhark/Internalise/Exps.hs view
@@ -2151,6 +2151,9 @@ typeExpForError :: E.TypeExp Info VName -> InternaliseM [ErrorMsgPart SubExp] typeExpForError (E.TEVar qn _) =   pure [ErrorString $ prettyText qn]+typeExpForError (E.TEParens te _) = do+  msg <- typeExpForError te+  pure $ ["("] <> msg <> [")"] typeExpForError (E.TEUnique te _) =   ("*" :) <$> typeExpForError te typeExpForError (E.TEDim dims te _) =
src/Language/Futhark/Parser/Parser.y view
@@ -488,13 +488,12 @@                 { $1 }  TypeExpAtom :: { UncheckedTypeExp }-             : '(' TypeExp ')'                { $2 }+             : '(' TypeExp ')'                { TEParens $2 (srcspan $1 $>) }              | '(' ')'                        { TETuple [] (srcspan $1 $>) }              | '(' TypeExp ',' TupleTypes ')' { TETuple ($2:$4) (srcspan $1 $>) }              | '{' '}'                        { TERecord [] (srcspan $1 $>) }              | '{' FieldTypes1 '}'            { TERecord $2 (srcspan $1 $>) }-             | SizeExp TypeExpTerm-               { TEArray $1 $2 (srcspan $1 $>) }+             | SizeExp TypeExpTerm            { TEArray $1 $2 (srcspan $1 $>) }              | QualName                       { TEVar (fst $1) (srclocOf (snd $1)) }  Constr :: { (Name, Loc) }
src/Language/Futhark/Pretty.hs view
@@ -175,6 +175,7 @@     where       ppField (name, t) = pretty (nameToString name) <> colon <+> pretty t   pretty (TEVar name _) = pretty name+  pretty (TEParens te _) = parens $ pretty te   pretty (TEApply t arg _) = pretty t <+> pretty arg   pretty (TEArrow (Just v) t1 t2 _) = parens v' <+> "->" <+> pretty t2     where@@ -376,11 +377,19 @@     p name = "." <> pretty name prettyExp _ (IndexSection idxs _ _) =   parens $ "." <> brackets (commasep (map pretty idxs))-prettyExp _ (Constr n cs t _) =-  "#" <> pretty n <+> sep (map pretty cs) <> prettyInst t+prettyExp p (Constr n cs t _) =+  parensIf (p >= 10) $+    "#" <> pretty n <+> sep (map (prettyExp 10) cs) <> prettyInst t prettyExp _ (Attr attr e _) =   prettyAttr attr </> prettyExp (-1) e-prettyExp i (AppExp e _) = prettyAppExp i e+prettyExp i (AppExp e res)+  | isEnvVarAtLeast "FUTHARK_COMPILER_DEBUGGING" 2,+    Just res' <- unAnnot res,+    not $ null $ appResExt res' =+      "#"+        <> brackets (commasep $ map prettyName $ appResExt res')+        </> parens (prettyAppExp i e)+  | otherwise = prettyAppExp i e  instance (Eq vn, IsName vn, Annot f) => Pretty (ExpBase f vn) where   pretty = prettyExp (-1)
src/Language/Futhark/Query.hs view
@@ -212,6 +212,8 @@     TEVar qn loc -> do       guard $ loc `contains` pos       Just $ RawAtName qn $ locOf loc+    TEParens te' _ ->+      atPosInTypeExp te' pos     TETuple es _ ->       msum $ map (`atPosInTypeExp` pos) es     TERecord fields _ ->
src/Language/Futhark/Syntax.hs view
@@ -421,11 +421,13 @@  deriving instance Ord (SizeExp Info VName) --- | An unstructured type with type variables and possibly shape--- declarations - this is what the user types in the source program.--- These are used to construct 'TypeBase's in the type checker.+-- | An unstructured syntactic type with type variables and possibly+-- shape declarations - this is what the user types in the source+-- program.  These are used to construct 'TypeBase's in the type+-- checker. data TypeExp f vn   = TEVar (QualName vn) SrcLoc+  | TEParens (TypeExp f vn) SrcLoc   | TETuple [TypeExp f vn] SrcLoc   | TERecord [(Name, TypeExp f vn)] SrcLoc   | TEArray (SizeExp f vn) (TypeExp f vn) SrcLoc@@ -452,6 +454,7 @@   locOf (TETuple _ loc) = locOf loc   locOf (TERecord _ loc) = locOf loc   locOf (TEVar _ loc) = locOf loc+  locOf (TEParens _ loc) = locOf loc   locOf (TEUnique _ loc) = locOf loc   locOf (TEApply _ _ loc) = locOf loc   locOf (TEArrow _ _ _ loc) = locOf loc
src/Language/Futhark/Traversals.hs view
@@ -242,6 +242,8 @@ instance ASTMappable (TypeExp Info VName) where   astMap tv (TEVar qn loc) =     TEVar <$> astMap tv qn <*> pure loc+  astMap tv (TEParens te loc) =+    TEParens <$> astMap tv te <*> pure loc   astMap tv (TETuple ts loc) =     TETuple <$> traverse (astMap tv) ts <*> pure loc   astMap tv (TERecord ts loc) =
src/Language/Futhark/TypeChecker.hs view
@@ -665,7 +665,7 @@           "size-polymorphic-entry"           "Entry point functions must not be size-polymorphic in their return type."   | p : _ <- filter nastyParameter params =-      warn loc $+      warn p $         "Entry point parameter\n"           </> indent 2 (pretty p)           </> "\nwill have an opaque type, so the entry point will likely not be callable."
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -16,6 +16,7 @@ import Control.Monad.Except import Control.Monad.Reader import Control.Monad.State+import Data.Bitraversable import Data.Either import Data.List (find, foldl', genericLength, partition) import Data.List.NonEmpty qualified as NE@@ -163,21 +164,36 @@   SrcLoc ->   UncheckedTypeExp ->   UncheckedExp ->-  TermTypeM (TypeExp Info VName, StructType, Exp, [VName])+  TermTypeM (TypeExp Info VName, StructType, Exp) checkCoerce loc te e = do-  (te', te_t, ext) <- checkTypeExpRigid te RigidCoerce+  (te', te_t, ext) <- checkTypeExpNonrigid te   e' <- checkExp e   e_t <- toStruct <$> expTypeFully e' -  (e_t_nonrigid, _) <--    allDimsFreshInType loc Nonrigid "coerce_d" e_t+  te_t_nonrigid <- makeNonExtFresh ext te_t    onFailure (CheckingAscription te_t e_t) $-    unify (mkUsage loc "type ascription") te_t e_t_nonrigid--  te_t' <- normTypeFully te_t+    unify (mkUsage loc "size coercion") e_t te_t_nonrigid -  pure (te', te_t', e', ext)+  -- If the type expression had any anonymous dimensions, these will+  -- now be in 'ext'.  Those we keep nonrigid and unify with e_t.+  -- This ensures that 'x :> [1][]i32' does not make the second+  -- dimension unknown.  Use of matchDims is sensible because the+  -- structure of e_t' will be fully known due to the unification, and+  -- te_t because type expressions are complete.+  pure (te', te_t, e')+  where+    makeNonExtFresh ext = bitraverse onDim pure+      where+        onDim d@(NamedSize v)+          | qualLeaf v `elem` ext = pure d+        onDim _ = do+          v <- newTypeName "coerce"+          constrain v . Size Nothing $+            mkUsage+              loc+              "a size coercion where the underlying expression size cannot be determined"+          pure $ NamedSize $ qualName v  unscopeType ::   SrcLoc ->@@ -313,10 +329,10 @@   (te', e') <- checkAscript loc te e   pure $ Ascript e' te' loc checkExp (AppExp (Coerce e te loc) _) = do-  (te', te_t, e', ext) <- checkCoerce loc te e+  (te', te_t, e') <- checkCoerce loc te e   t <- expTypeFully e'   t' <- matchDims (const . const pure) t $ fromStruct te_t-  pure $ AppExp (Coerce e' te' loc) (Info $ AppRes t' ext)+  pure $ AppExp (Coerce e' te' loc) (Info $ AppRes t' []) checkExp (AppExp (BinOp (op, oploc) NoInfo (e1, _) (e2, _) loc) NoInfo) = do   (op', ftype) <- lookupVar oploc op   e1_arg <- checkArg e1
src/Language/Futhark/TypeChecker/Terms/DoLoop.hs view
@@ -13,6 +13,7 @@ import Control.Monad.State import Data.Bifunctor import Data.Bitraversable+import Data.List qualified as L import Data.Map.Strict qualified as M import Data.Maybe import Data.Set qualified as S@@ -26,21 +27,39 @@ import Language.Futhark.TypeChecker.Unify import Prelude hiding (mod) +-- | Retrieve an oracle that can be used to decide whether two are in+-- the same equivalence class (i.e. have been unified).  This is an+-- exotic operation.+getAreSame :: MonadUnify m => m (VName -> VName -> Bool)+getAreSame = check <$> getConstraints+  where+    check constraints x y =+      case (M.lookup x constraints, M.lookup y constraints) of+        (Just (_, Size (Just (NamedSize x')) _), _) ->+          check constraints (qualLeaf x') y+        (_, Just (_, Size (Just (NamedSize y')) _)) ->+          check constraints x (qualLeaf y')+        _ ->+          x == y+ -- | Replace specified sizes with distinct fresh size variables. someDimsFreshInType ::   SrcLoc ->   Rigidity ->   Name ->-  S.Set VName ->+  [VName] ->   TypeBase Size als ->   TermTypeM (TypeBase Size als)-someDimsFreshInType loc r desc sizes = bitraverse onDim pure+someDimsFreshInType loc r desc fresh t = do+  areSameSize <- getAreSame+  let freshen v = any (areSameSize v) fresh+  bitraverse (onDim freshen) pure t   where-    onDim (NamedSize d)-      | qualLeaf d `S.member` sizes = do+    onDim freshen (NamedSize d)+      | freshen $ qualLeaf d = do           v <- newDimVar loc r desc           pure $ NamedSize $ qualName v-    onDim d = pure d+    onDim _ d = pure d  -- | Replace the specified sizes with fresh size variables of the -- specified ridigity.  Returns the new fresh size variables.@@ -48,14 +67,16 @@   SrcLoc ->   Rigidity ->   Name ->-  S.Set VName ->+  [VName] ->   TypeBase Size als ->   TermTypeM (TypeBase Size als, [VName])-freshDimsInType loc r desc sizes t =-  second M.elems <$> runStateT (bitraverse onDim pure t) mempty+freshDimsInType loc r desc fresh t = do+  areSameSize <- getAreSame+  let freshen v = any (areSameSize v) fresh+  second M.elems <$> runStateT (bitraverse (onDim freshen) pure t) mempty   where-    onDim (NamedSize d)-      | qualLeaf d `S.member` sizes = do+    onDim freshen (NamedSize d)+      | freshen $ qualLeaf d = do           prev_subst <- gets $ M.lookup $ qualLeaf d           case prev_subst of             Just d' -> pure $ NamedSize $ qualName d'@@ -63,7 +84,7 @@               v <- lift $ newDimVar loc r desc               modify $ M.insert (qualLeaf d) v               pure $ NamedSize $ qualName v-    onDim d = pure d+    onDim _ d = pure d  -- | Mark bindings of names in "consumed" as Unique. uniquePat :: Names -> Pat -> Pat@@ -166,15 +187,11 @@ wellTypedLoopArg :: ArgSource -> [VName] -> Pat -> Exp -> TermTypeM () wellTypedLoopArg src sparams pat arg = do   (merge_t, _) <--    freshDimsInType (srclocOf arg) Nonrigid "loop" (S.fromList sparams) $-      toStruct $-        patternType pat+    freshDimsInType (srclocOf arg) Nonrigid "loop" sparams $+      toStruct (patternType pat)   arg_t <- toStruct <$> expTypeFully arg   onFailure (checking merge_t arg_t) $-    unify-      (mkUsage (srclocOf arg) desc)-      merge_t-      arg_t+    unify (mkUsage (srclocOf arg) desc) merge_t arg_t   where     (checking, desc) =       case src of@@ -203,6 +220,7 @@   TermTypeM (CheckedLoop, AppRes) checkDoLoop checkExp (mergepat, mergeexp, form, loopbody) loc =   sequentially (checkExp mergeexp) $ \mergeexp' _ -> do+    known_before <- M.keysSet <$> getConstraints     zeroOrderType       (mkUsage (srclocOf mergeexp) "use as loop variable")       "type used as loop variable"@@ -236,17 +254,18 @@     -- We don't want the loop parameters to alias their initial     -- values, so we blank them here.  We will actually check them     -- properly later.-    (merge_t, new_dims_to_initial_dim) <-+    (merge_t, new_dims_map) <-       -- dim handling (1)-      allDimsFreshInType loc Nonrigid "loop" . flip setAliases mempty+      allDimsFreshInType loc Nonrigid "loop_d" . flip setAliases mempty         =<< expTypeFully mergeexp'-    let new_dims = M.keys new_dims_to_initial_dim+    let new_dims_to_initial_dim = M.toList new_dims_map+        new_dims = map fst new_dims_to_initial_dim      -- dim handling (2)     let checkLoopReturnSize mergepat' loopbody' = do           loopbody_t <- expTypeFully loopbody'           pat_t <--            someDimsFreshInType loc Nonrigid "loop" (S.fromList new_dims)+            someDimsFreshInType loc Nonrigid "loop" new_dims               =<< normTypeFully (patternType mergepat')            -- We are ignoring the dimensions here, because any mismatches@@ -260,20 +279,22 @@           -- 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.+          areSameSize <- getAreSame           let onDims _ x y                 | x == y = pure x               onDims _ (NamedSize v) d-                | qualLeaf v `elem` new_dims = do-                    case M.lookup (qualLeaf v) new_dims_to_initial_dim of-                      Just d'-                        | d' == d ->-                            modify $ first $ M.insert (qualLeaf v) (SizeSubst d)-                      _ ->-                        modify $ second (qualLeaf v :)+                | Just (v', d') <-+                    L.find (areSameSize (qualLeaf v) . fst) new_dims_to_initial_dim = do+                    if d' == d+                      then modify $ first $ M.insert v' (SizeSubst d)+                      else+                        unless (qualLeaf v `S.member` known_before) $+                          modify (second (qualLeaf v :))                     pure $ NamedSize v               onDims _ x _ = pure x           loopbody_t' <- normTypeFully loopbody_t           merge_t' <- normTypeFully merge_t+           let (init_substs, sparams) =                 execState (matchDims onDims merge_t' loopbody_t') mempty @@ -319,7 +340,7 @@           bound_t <- expTypeFully uboundexp'           bindingIdent i bound_t $ \i' ->             noUnique . bindingPat [] mergepat (Ascribed merge_t) $-              \mergepat' -> tapOccurrences $ do+              \mergepat' -> tapOccurrences $ incLevel $ do                 loopbody' <- noSizeEscape $ checkExp loopbody                 (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'                 pure@@ -337,7 +358,7 @@               | Just t' <- peelArray 1 t ->                   bindingPat [] xpat (Ascribed t') $ \xpat' ->                     noUnique . bindingPat [] mergepat (Ascribed merge_t) $-                      \mergepat' -> tapOccurrences $ do+                      \mergepat' -> tapOccurrences $ incLevel $ do                         loopbody' <- noSizeEscape $ checkExp loopbody                         (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'                         pure@@ -353,6 +374,7 @@         While cond ->           noUnique . bindingPat [] mergepat (Ascribed merge_t) $ \mergepat' ->             tapOccurrences+              . incLevel               . sequentially                 ( checkExp cond                     >>= unifies "being the condition of a 'while' loop" (Scalar $ Prim Bool)@@ -390,7 +412,7 @@     wellTypedLoopArg Initial sparams mergepat'' mergeexp'      (loopt, retext) <--      freshDimsInType loc (Rigid RigidLoop) "loop" (S.fromList sparams) $+      freshDimsInType loc (Rigid RigidLoop) "loop" sparams $         loopReturnType mergepat'' merge_t'     -- We set all of the uniqueness to be unique.  This is intentional,     -- and matches what happens for function calls.  Those arrays that
src/Language/Futhark/TypeChecker/Types.hs view
@@ -273,6 +273,10 @@           <+> dquotes (hsep (pretty name : map pretty ps))           <+> "used without any arguments." --+evalTypeExp (TEParens te loc) = do+  (te', svars, ts, ls) <- evalTypeExp te+  pure (TEParens te' loc, svars, ts, ls)+-- evalTypeExp (TETuple ts loc) = do   (ts', svars, ts_s, ls) <- unzip4 <$> mapM evalTypeExp ts   pure@@ -575,6 +579,7 @@       check seen t     check _ TEArray {} = pure ()     check _ TEVar {} = pure ()+    check seen (TEParens te _) = check seen te  -- | @checkTypeParams ps m@ checks the type parameters @ps@, then -- invokes the continuation @m@ with the checked parameters, while