[Git][ghc/ghc][master] JS: fix and enhance non-minimized code generation (#22455)

Marge Bot (@marge-bot) gitlab at gitlab.haskell.org
Sat Jun 3 03:54:24 UTC 2023



Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC


Commits:
f53ac0ae by Sylvain Henry at 2023-06-02T23:54:01-04:00
JS: fix and enhance non-minimized code generation (#22455)

Flag -ddisable-js-minimizer was producing invalid code. Fix that and
also a few other things to generate nicer JS code for debugging.

The added test checks that we don't regress when using the flag.

- - - - -


11 changed files:

- compiler/GHC/JS/Make.hs
- compiler/GHC/JS/Ppr.hs
- compiler/GHC/StgToJS/Apply.hs
- compiler/GHC/StgToJS/Closure.hs
- compiler/GHC/StgToJS/CodeGen.hs
- compiler/GHC/StgToJS/Linker/Linker.hs
- compiler/GHC/StgToJS/Linker/Opt.hs
- compiler/GHC/StgToJS/Rts/Rts.hs
- + testsuite/tests/javascript/T22455.hs
- + testsuite/tests/javascript/T22455.stdout
- testsuite/tests/javascript/all.T


Changes:

=====================================
compiler/GHC/JS/Make.hs
=====================================
@@ -83,7 +83,7 @@ module GHC.JS.Make
   -- $intro_funcs
   , var
   , jString
-  , jLam, jFunction, jVar, jFor, jForNoDecl, jForIn, jForEachIn, jTryCatchFinally
+  , jLam, jFun, jFunction, jVar, jFor, jForNoDecl, jForIn, jForEachIn, jTryCatchFinally
   -- * Combinators
   -- $combinators
   , (||=), (|=), (.==.), (.===.), (.!=.), (.!==.), (.!)
@@ -249,6 +249,15 @@ jLam f = ValExpr . UnsatVal . IS $ do
            (block,is) <- runIdentSupply $ toSat_ f []
            return $ JFunc is block
 
+-- | Create a new function. The result is a 'GHC.JS.Syntax.JStat'.
+-- Usage:
+--
+-- > jFun fun_name $ \x -> ...
+jFun :: ToSat a => Ident -> a -> JStat
+jFun n f = UnsatBlock . IS $ do
+           (block,is) <- runIdentSupply $ toSat_ f []
+           return $ FuncStat n is block
+
 -- | Introduce a new variable into scope for the duration
 -- of the enclosed expression. The result is a block statement.
 -- Usage:


=====================================
compiler/GHC/JS/Ppr.hs
=====================================
@@ -60,7 +60,6 @@ module GHC.JS.Ppr
   , jsToDoc
   , pprStringLit
   , interSemi
-  , addSemi
   , braceNest
   , hangBrace
   )
@@ -138,15 +137,25 @@ instance JsToDoc [JStat] where jsToDocR r = jcat . map (addSemi . jsToDocR r)
 
 defRenderJsS :: JsRender doc => RenderJs doc -> JStat -> doc
 defRenderJsS r = \case
-  IfStat cond x y -> hangBrace (text "if" <+?> parens (jsToDocR r cond))
-                      (jnest $ optBlock r x)
-                      <+?> mbElse
+  IfStat cond x y -> jcat
+                        [ hangBrace (text "if" <+?> parens (jsToDocR r cond)) (optBlock r x)
+                        , mbElse
+                        ]
         where mbElse | y == BlockStat []  = empty
-                     | otherwise = hangBrace (text "else") (jnest $ optBlock r y)
+                     | otherwise = hangBrace (text "else") (optBlock r y)
   DeclStat x Nothing  -> text "var" <+> jsToDocR r x
+    -- special treatment for functions, otherwise there is too much left padding
+    -- (more than the length of the expression assigned to). E.g.
+    --
+    --    var long_variable_name = (function()
+    --                               {
+    --                               ...
+    --                             });
+    --
+  DeclStat x (Just (ValExpr f@(JFunc {}))) -> jhang (text "var" <+> jsToDocR r x <+?> char '=') (jsToDocR r f)
   DeclStat x (Just e) -> text "var" <+> jsToDocR r x <+?> char '=' <+?> jsToDocR r e
-  WhileStat False p b -> hangBrace (text "while" <+?> parens (jsToDocR r p)) (jnest $ optBlock r b)
-  WhileStat True  p b -> hangBrace (text "do") (jnest $ optBlock r b) <+?> text "while" <+?> parens (jsToDocR r p)
+  WhileStat False p b -> hangBrace (text "while" <+?> parens (jsToDocR r p)) (optBlock r b)
+  WhileStat True  p b -> hangBrace (text "do") (optBlock r b) <+?> text "while" <+?> parens (jsToDocR r p)
   BreakStat l         -> addSemi $ maybe (text "break")    (\(LexicalFastString s) -> (text "break"    <+> ftext s)) l
   ContinueStat l      -> addSemi $ maybe (text "continue") (\(LexicalFastString s) -> (text "continue" <+> ftext s)) l
   LabelStat (LexicalFastString l) s -> ftext l <> char ':' $$$ printBS s
@@ -154,10 +163,10 @@ defRenderJsS r = \case
           printBS (BlockStat ss) = interSemi $ map (jsToDocR r) ss
           printBS x = jsToDocR r x
 
-  ForStat init p s1 sb -> hangBrace (text "for" <+?> parens forCond) (jnest $ optBlock r sb)
+  ForStat init p s1 sb -> hangBrace (text "for" <+?> parens forCond) (optBlock r sb)
     where
       forCond = jsToDocR r init <> semi <+?> jsToDocR r p <> semi <+?> parens (jsToDocR r s1)
-  ForInStat each i e b -> hangBrace (text txt <+?> parens (jsToDocR r i <+> text "in" <+> jsToDocR r e)) (jnest $ optBlock r b)
+  ForInStat each i e b -> hangBrace (text txt <+?> parens (jsToDocR r i <+> text "in" <+> jsToDocR r e)) (optBlock r b)
         where txt | each = "for each"
                   | otherwise = "for"
   SwitchStat e l d     -> hangBrace (text "switch" <+?> parens (jsToDocR r e)) cases
@@ -168,33 +177,35 @@ defRenderJsS r = \case
   ApplStat e es     -> jsToDocR r e <> (parens . foldl' (<+?>) empty . punctuate comma $ map (jsToDocR r) es)
   FuncStat i is b   -> hangBrace (text "function" <+> jsToDocR r i
                                   <> parens (foldl' (<+?>) empty . punctuate comma . map (jsToDocR r) $ is))
-                             (jnest $ optBlock r b)
+                             (optBlock r b)
   TryStat s i s1 s2 -> hangBrace (text "try") (jsToDocR r s) <+?> mbCatch <+?> mbFinally
         where mbCatch | s1 == BlockStat [] = empty
-                      | otherwise = hangBrace (text "catch" <+?> parens (jsToDocR r i)) (jnest $ optBlock r s1)
+                      | otherwise = hangBrace (text "catch" <+?> parens (jsToDocR r i)) (optBlock r s1)
               mbFinally | s2 == BlockStat [] = empty
-                        | otherwise = hangBrace (text "finally") (jnest $ optBlock r s2)
+                        | otherwise = hangBrace (text "finally") (optBlock r s2)
   AssignStat i op x    -> case x of
     -- special treatment for functions, otherwise there is too much left padding
     -- (more than the length of the expression assigned to). E.g.
     --
-    --    var long_variable_name = (function()
+    --    long_variable_name = (function()
     --                               {
     --                               ...
     --                             });
     --
-    ValExpr (JFunc is b) -> jsToDocR r i <> ftext (aOpText op) <> text " function" <> parens (foldl' (<+?>) empty . punctuate comma . map (jsToDocR r) $ is) <> braceNest (jsToDocR r b)
-    _                      -> jsToDocR r i <+?> ftext (aOpText op) <+?> jsToDocR r x
+    ValExpr f@(JFunc {}) -> jhang (jsToDocR r i <> ftext (aOpText op)) (jsToDocR r f)
+    _                    -> jsToDocR r i <+?> ftext (aOpText op) <+?> jsToDocR r x
   UOpStat op x
     | isPre op && isAlphaOp op -> ftext (uOpText op) <+> optParens r x
     | isPre op                 -> ftext (uOpText op) <+> optParens r x
     | otherwise                -> optParens r x <+> ftext (uOpText op)
   BlockStat xs -> jsToDocR r xs
 
+-- | Remove one Block layering if we know we already have braces around the
+-- statement
 optBlock :: JsRender doc => RenderJs doc -> JStat -> doc
 optBlock r x = case x of
   BlockStat{} -> jsToDocR r x
-  _           -> addSemi $ jsToDocR r x
+  _           -> addSemi (jsToDocR r x)
 
 optParens :: JsRender doc => RenderJs doc -> JExpr -> doc
 optParens r x = case x of
@@ -338,9 +349,6 @@ encodeJsonChar = \case
 interSemi :: JsRender doc => [doc] -> doc
 interSemi = foldl ($$$) empty . punctuateFinal semi semi
 
-addSemi :: IsLine doc => doc -> doc
-addSemi x = x <> semi <> char '\n'
-
 -- | The structure `{body}`, optionally indented over multiple lines
 {-# INLINE braceNest #-}
 braceNest :: JsRender doc => doc -> doc
@@ -349,7 +357,11 @@ braceNest x = lbrace $$$ jnest x $$$ rbrace
 -- | The structure `hdr {body}`, optionally indented over multiple lines
 {-# INLINE hangBrace #-}
 hangBrace :: JsRender doc => doc -> doc -> doc
-hangBrace hdr body = hdr <+?> braceNest body
+hangBrace hdr body = jcat [ hdr <> char ' ' <> char '{', jnest body, char '}' ]
+
+{-# INLINE jhang #-}
+jhang :: JsRender doc => doc -> doc -> doc
+jhang hdr body = jcat [ hdr, jnest body]
 
 -- | JsRender controls the differences in whitespace between HLine and SDoc.
 -- Generally, this involves the indentation and newlines in the human-readable
@@ -365,16 +377,21 @@ class IsLine doc => JsRender doc where
   jcat      :: [doc] -> doc
   -- | Optionally indent the following
   jnest     :: doc -> doc
+  -- | Append semi-colon (and line-break in HLine mode)
+  addSemi   :: doc -> doc
 
 instance JsRender SDoc where
   (<+?>) = (<+>)
   {-# INLINE (<+?>) #-}
-  ($$$)  = ($$)
+  ($$$)  = ($+$)
   {-# INLINE ($$$) #-}
   jcat               = vcat
   {-# INLINE jcat #-}
   jnest              = nest 2
   {-# INLINE jnest #-}
+  addSemi x = x <> semi
+  {-# INLINE addSemi #-}
+
 
 instance JsRender HLine where
   (<+?>) = (<>)
@@ -385,3 +402,6 @@ instance JsRender HLine where
   {-# INLINE jcat #-}
   jnest              = id
   {-# INLINE jnest #-}
+  addSemi x = x <> semi <> char '\n'
+  -- we add a line-break to avoid issues with lines too long in minified outputs
+  {-# INLINE addSemi #-}


=====================================
compiler/GHC/StgToJS/Apply.hs
=====================================
@@ -590,7 +590,7 @@ genericStackApply cfg = closure info body
 --
 genericFastApply :: StgToJSConfig -> JStat
 genericFastApply s =
-   TxtI "h$ap_gen_fast" ||= jLam \tag -> jVar \c ->
+   jFun (TxtI "h$ap_gen_fast") \tag -> jVar \c ->
       [traceRts s (jString "h$ap_gen_fast: " + tag)
       , c |= closureEntry r1
       , SwitchStat (entryClosureType c)
@@ -802,12 +802,12 @@ stackApply s fun_name nargs nvars =
 -- h$ap_n_r_fast is entered if a function of unknown arity is called, n
 -- arguments are already in r registers
 fastApply :: StgToJSConfig -> FastString -> Int -> Int -> JStat
-fastApply s fun_name nargs nvars = func ||= body0
+fastApply s fun_name nargs nvars = body0
   where
       -- special case for h$ap_0_0_fast
       body0 = if nargs == 0 && nvars == 0
-        then jLam (enter s r1)
-        else toJExpr (JFunc myFunArgs body)
+        then jFun func (enter s r1)
+        else FuncStat func myFunArgs body
 
       func    = TxtI fun_name
 
@@ -875,7 +875,7 @@ fastApply s fun_name nargs nvars = func ||= body0
 
 zeroApply :: StgToJSConfig -> JStat
 zeroApply s = mconcat
-  [ TxtI "h$e" ||= jLam (\c -> (r1 |= c) <> enter s c)
+  [ jFun (TxtI "h$e") (\c -> (r1 |= c) <> enter s c)
   ]
 
 -- carefully enter a closure that might be a thunk or a function
@@ -973,13 +973,13 @@ selectors s =
 
     mkSel :: FastString -> (JExpr -> JExpr) -> JStat
     mkSel name sel = mconcat
-      [TxtI createName ||= jLam \r -> mconcat
+      [jFun (TxtI createName) \r -> mconcat
           [ traceRts s (toJExpr ("selector create: " <> name <> " for ") + (r .^ "alloc"))
           , ifS (isThunk r .||. isBlackhole r)
               (returnS (app "h$mkSelThunk" [r, toJExpr (v entryName), toJExpr (v resName)]))
               (returnS (sel r))
           ]
-      , TxtI resName ||= jLam \r -> mconcat
+      , jFun (TxtI resName) \r -> mconcat
           [ traceRts s (toJExpr ("selector result: " <> name <> " for ") + (r .^ "alloc"))
           , returnS (sel r)
           ]
@@ -1106,7 +1106,7 @@ papGen cfg =
 -- general utilities
 -- move the first n registers, starting at R2, m places up (do not use with negative m)
 moveRegs2 :: JStat
-moveRegs2 = TxtI "h$moveRegs2" ||= jLam moveSwitch
+moveRegs2 = jFun (TxtI "h$moveRegs2") moveSwitch
   where
     moveSwitch n m = SwitchStat ((n .<<. 8) .|. m) switchCases (defaultCase n m)
     -- fast cases


=====================================
compiler/GHC/StgToJS/Closure.hs
=====================================
@@ -116,7 +116,7 @@ setObjInfo debug obj t name fields a size regs static
 closure :: ClosureInfo -- ^ object being info'd see @ciVar@ in @ClosureInfo@
         -> JStat       -- ^ rhs
         -> JStat
-closure ci body = (ciVar ci ||= jLam body) `mappend` closureInfoStat False ci
+closure ci body = (jFun (ciVar ci) body) `mappend` closureInfoStat False ci
 
 conClosure :: Ident -> FastString -> CILayout -> Int -> JStat
 conClosure symbol name layout constr =


=====================================
compiler/GHC/StgToJS/CodeGen.hs
=====================================
@@ -366,4 +366,4 @@ genToplevelRhs i rhs = case rhs of
                                  sr)
     ccId <- costCentreStackLbl cc
     emitStatic idt static ccId
-    return $ (eid ||= toJExpr (JFunc [] (ll <> upd <> setcc <> body)))
+    return $ (FuncStat eid [] (ll <> upd <> setcc <> body))


=====================================
compiler/GHC/StgToJS/Linker/Linker.hs
=====================================
@@ -198,12 +198,7 @@ link lc_cfg cfg logger unit_env out _include units objFiles jsFiles isRootFun ex
       -- link generated RTS parts into rts.js
       unless (lcNoRts lc_cfg) $ do
         withFile (out </> "rts.js") WriteMode $ \h -> do
-         if csPrettyRender cfg
-          then printSDoc defaultJsContext (Ppr.PageMode True) h (rtsDeclsText $$ rtsText cfg)
-          else do
-            bh <- newBufHandle h
-            bPutHDoc bh defaultJsContext (line rtsDeclsText $$ line (rtsText cfg))
-            bFlush bh
+          void $ hPutJS (csPrettyRender cfg) h (rts cfg)
 
       -- link dependencies' JS files into lib.js
       withBinaryFile (out </> "lib.js") WriteMode $ \h -> do
@@ -307,6 +302,24 @@ data CompactedModuleCode = CompactedModuleCode
   , cmc_exports :: !B.ByteString        -- ^ rendered exports
   }
 
+-- | Output JS statements and return the output size in bytes.
+hPutJS :: Bool -> Handle -> Sat.JStat -> IO Integer
+hPutJS render_pretty h = \case
+  Sat.BlockStat [] -> pure 0
+  x                -> do
+    before <- hTell h
+    if render_pretty
+      then do
+        printSDoc defaultJsContext (Ppr.PageMode True) h (pretty render_pretty x)
+      else do
+        bh <- newBufHandle h
+        bPutHDoc bh defaultJsContext (line $ pretty render_pretty x)
+        bFlush bh
+    -- Append an empty line to correctly end the file in a newline
+    hPutChar h '\n'
+    after <- hTell h
+    pure $! (after - before)
+
 -- | Link modules and pretty-print them into the given Handle
 renderLinker
   :: Handle
@@ -321,18 +334,7 @@ renderLinker h render_pretty mods jsFiles = do
 
   let
     putBS   = B.hPut h
-    putJS x = do
-      before <- hTell h
-      if render_pretty
-        then do
-          printSDoc defaultJsContext (Ppr.PageMode True) h (pretty x)
-        else do
-          bh <- newBufHandle h
-          -- Append an empty line to correctly end the file in a newline
-          bPutHDoc bh defaultJsContext ((line $ pretty x) $$ empty)
-          bFlush bh
-      after <- hTell h
-      pure $! (after - before)
+    putJS   = hPutJS render_pretty h
 
   ---------------------------------------------------------
   -- Pretty-print JavaScript code for all the dependencies.


=====================================
compiler/GHC/StgToJS/Linker/Opt.hs
=====================================
@@ -20,7 +20,7 @@
 -----------------------------------------------------------------------------
 module GHC.StgToJS.Linker.Opt
   ( pretty
-  , ghcjsRenderJs
+  , optRenderJs
   )
 where
 
@@ -39,11 +39,17 @@ import Data.List (sortOn)
 import Data.Char (isAlpha,isDigit,ord)
 import qualified Data.ByteString.Short as SBS
 
-pretty :: JsRender doc => JStat -> doc
-pretty = jsToDocR ghcjsRenderJs
-
-ghcjsRenderJs :: RenderJs doc
-ghcjsRenderJs = defaultRenderJs
+pretty :: JsRender doc => Bool -> JStat -> doc
+pretty render_pretty = \case
+  BlockStat []      -> empty
+  s | render_pretty -> jsToDocR defaultRenderJs [s]
+    | otherwise     -> jsToDocR optRenderJs [s]
+                        -- render as a list of statements to ensure that
+                        -- semicolons are added.
+
+-- | Render JS with code size minimization enabled
+optRenderJs :: RenderJs doc
+optRenderJs = defaultRenderJs
   { renderJsV = ghcjsRenderJsV
   , renderJsS = ghcjsRenderJsS
   , renderJsI = ghcjsRenderJsI


=====================================
compiler/GHC/StgToJS/Rts/Rts.hs
=====================================
@@ -1,7 +1,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TypeApplications  #-}
-
-{-# OPTIONS_GHC -O0 #-}
+{-# LANGUAGE BlockArguments    #-}
 
 -----------------------------------------------------------------------------
 -- |
@@ -24,7 +23,11 @@
 --
 -----------------------------------------------------------------------------
 
-module GHC.StgToJS.Rts.Rts where
+module GHC.StgToJS.Rts.Rts
+  ( rts
+  , assignRegs
+  )
+where
 
 import GHC.Prelude
 
@@ -42,11 +45,8 @@ import GHC.StgToJS.Regs
 import GHC.StgToJS.Types
 import GHC.StgToJS.Stack
 
-import GHC.StgToJS.Linker.Opt
-
 import GHC.Data.FastString
 import GHC.Types.Unique.Map
-import GHC.JS.Ppr
 
 import Data.Array
 import Data.Monoid
@@ -56,8 +56,8 @@ import qualified Data.Bits          as Bits
 -- | The garbageCollector resets registers and result variables.
 garbageCollector :: JStat
 garbageCollector =
-  mconcat [ TxtI "h$resetRegisters"  ||= jLam (mconcat $ map resetRegister [minBound..maxBound])
-          , TxtI "h$resetResultVars" ||= jLam (mconcat $ map resetResultVar [minBound..maxBound])
+  mconcat [ jFun (TxtI "h$resetRegisters")  (mconcat $ map resetRegister [minBound..maxBound])
+          , jFun (TxtI "h$resetResultVars") (mconcat $ map resetResultVar [minBound..maxBound])
           ]
 
 -- | Reset the register 'r' in JS Land. Note that this "resets" by setting the
@@ -233,8 +233,8 @@ declRegs =
 -- | JS payload to define getters and setters on the registers.
 regGettersSetters :: JStat
 regGettersSetters =
-  mconcat [ TxtI "h$getReg" ||= jLam (\n   -> SwitchStat n getRegCases mempty)
-          , TxtI "h$setReg" ||= jLam (\n v -> SwitchStat n (setRegCases v) mempty)
+  mconcat [ jFun (TxtI "h$getReg") (\n   -> SwitchStat n getRegCases mempty)
+          , jFun (TxtI "h$setReg") (\n v -> SwitchStat n (setRegCases v) mempty)
           ]
   where
     getRegCases =
@@ -292,17 +292,16 @@ closureTypes = mconcat (map mkClosureType (enumFromTo minBound maxBound)) <> clo
     mkClosureType c = let s = TxtI . mkFastString $ "h$" ++ map toUpper (show c) ++ "_CLOSURE"
                       in  s ||= toJExpr c
     closureTypeName :: JStat
-    closureTypeName =
-      TxtI "h$closureTypeName" ||= jLam (\c ->
-                                           mconcat (map (ifCT c) [minBound..maxBound])
-                                          <> returnS (jString "InvalidClosureType"))
+    closureTypeName = jFun (TxtI "h$closureTypeName") \c ->
+                        mconcat (map (ifCT c) [minBound..maxBound])
+                        <> returnS (jString "InvalidClosureType")
 
     ifCT :: JExpr -> ClosureType -> JStat
     ifCT arg ct = jwhenS (arg .===. toJExpr ct) (returnS (toJExpr (show ct)))
 
 -- | JS payload declaring the RTS functions.
-rtsDecls :: Sat.JStat
-rtsDecls = satJStat (Just "h$RTSD") $
+rtsDecls :: JStat
+rtsDecls =
   mconcat [ TxtI "h$currentThread"   ||= null_                   -- thread state object for current thread
           , TxtI "h$stack"           ||= null_                   -- stack for the current thread
           , TxtI "h$sp"              ||= 0                       -- stack pointer for the current thread
@@ -315,17 +314,12 @@ rtsDecls = satJStat (Just "h$RTSD") $
           , declRegs
           , declRets]
 
--- | print the embedded RTS to a String
-rtsText :: forall doc. JsRender doc => StgToJSConfig -> doc
-rtsText = pretty @doc . jsOptimize . rts
-
--- | print the RTS declarations to a String.
-rtsDeclsText :: forall doc. JsRender doc => doc
-rtsDeclsText = pretty @doc . jsOptimize $ rtsDecls
-
--- | Wrapper over the RTS to guarentee saturation, see 'GHC.JS.Transform'
+-- | Generated RTS code
 rts :: StgToJSConfig -> Sat.JStat
-rts = satJStat (Just "h$RTS") . rts'
+rts cfg = jsOptimize $ satJStat (Just "h$RTS") $ mconcat
+  [ rtsDecls
+  , rts' cfg
+  ]
 
 -- | JS Payload which defines the embedded RTS.
 rts' :: StgToJSConfig -> JStat
@@ -349,8 +343,8 @@ rts' s =
           , TxtI "h$vt_rtsobj" ||= toJExpr RtsObjV
           , TxtI "h$vt_obj"    ||= toJExpr ObjV
           , TxtI "h$vt_arr"    ||= toJExpr ArrV
-          , TxtI "h$bh"        ||= jLam (bhStats s True)
-          , TxtI "h$bh_lne"    ||= jLam (\x frameSize -> bhLneStats s x frameSize)
+          , jFun (TxtI "h$bh")     (bhStats s True)
+          , jFun (TxtI "h$bh_lne") (\x frameSize -> bhLneStats s x frameSize)
           , closure (ClosureInfo (TxtI "h$blackhole") (CIRegs 0 []) "blackhole" (CILayoutUnknown 2) CIBlackhole mempty)
                (appS "throw" [jString "oops: entered black hole"])
           , closure (ClosureInfo (TxtI "h$blackholeTrap") (CIRegs 0 []) "blackhole" (CILayoutUnknown 2) CIThunk mempty)


=====================================
testsuite/tests/javascript/T22455.hs
=====================================
@@ -0,0 +1,4 @@
+module Main where
+
+main :: IO ()
+main = putStrLn "Hello World"


=====================================
testsuite/tests/javascript/T22455.stdout
=====================================
@@ -0,0 +1 @@
+Hello World


=====================================
testsuite/tests/javascript/all.T
=====================================
@@ -17,3 +17,4 @@ test('js-callback04', js_skip, compile_and_run, [''])
 test('js-callback05', js_skip, compile_and_run, [''])
 
 test('T23346', normal, compile_and_run, [''])
+test('T22455', normal, compile_and_run, ['-ddisable-js-minifier'])



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f53ac0ae30adf699dae02131bcece8be074d9737

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f53ac0ae30adf699dae02131bcece8be074d9737
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20230602/5a797b3e/attachment-0001.html>


More information about the ghc-commits mailing list