[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 3 commits: Generalize so_inline to specify which bindings should be preserved
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC Commits: 1e60023b by Facundo Domínguez at 2026-05-07T18:01:16-04:00 Generalize so_inline to specify which bindings should be preserved This commit generalizes the so_inline option of the simple optimizer so we can indicate with a predicate the specific bindings that should be kept. This feature is important for the LiquidHaskell plugin, which relies on the simple optimizer to make core programs easier to read, but needs to preserve bindings that are relevant for verification. See https://gitlab.haskell.org/ghc/ghc/-/issues/24386 for the full discussion. - - - - - ba12801b by Wolfgang Jeltsch at 2026-05-11T07:00:10-04:00 Move the `Text.Read` implementation into `base` - - - - - ede968b6 by Alice Rixte at 2026-05-11T07:00:17-04:00 Script for downloading and copying `base-exports` file - - - - - 18 changed files: - + changelog.d/so_inline_is_a_predicate - compiler/GHC/Core/SimpleOpt.hs - compiler/GHC/Driver/Config.hs - libraries/base/src/Data/Functor/Classes.hs - libraries/base/src/Data/Functor/Compose.hs - libraries/base/src/Prelude.hs - libraries/base/src/Text/Read.hs - libraries/ghc-internal/ghc-internal.cabal.in - libraries/ghc-internal/src/GHC/Internal/IO/Encoding.hs - − libraries/ghc-internal/src/GHC/Internal/Text/Read.hs - + testsuite/tests/ghc-api/T24386.hs - testsuite/tests/ghc-api/all.T - + testsuite/tests/interface-stability/.gitignore - testsuite/tests/interface-stability/README.mkd - + testsuite/tests/interface-stability/download-base-exports.sh - testsuite/tests/th/T24111.stdout - testsuite/tests/typecheck/should_compile/subsumption_sort_hole_fits.stderr - testsuite/tests/typecheck/should_fail/T21130.stderr Changes: ===================================== changelog.d/so_inline_is_a_predicate ===================================== @@ -0,0 +1,12 @@ +section: ghc-lib +synopsis: Generalize the ``so_inline`` option of the simple optimizer to a predicate + that selects the bindings to preserve. + +issues: #24386 +mrs: !15988 + +description: { + The ``so_inline`` option of the simple optimizer was a boolean and now it is a + predicate taking a binding ``Id`` and returning a boolean. ``const b`` has the + same effect as formerly setting ``b``. +} ===================================== compiler/GHC/Core/SimpleOpt.hs ===================================== @@ -108,6 +108,93 @@ unfolding-info to the scrutinee's Id.) * Bad bad bad: then the x in case x of ... may be replaced with a version that has an unfolding. See ticket #25790 + +Note [Controlling inlining in the simple optimiser] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sometimes, plugins that analyse Core programs may want to prevent the +inlining of certain bindings. While they could avoid running the simple +optimiser at all, that would leave plenty of generated bindings that do not +have a direct correspondence to the source code. + +For example, consider the following Haskell code: + + foo = z + where + z = z1 + z2 + z1 = 42 + z2 = 1 + +Before the simple optimizer runs, the Core programs is roughly: + + foo = + let + foo_aIb = + let + z2 + = let + z2_aHG = 1 + in + z2_aHG + in + let + z1 = + let + z1_aHR = 42 + in + z1_aHR + in + let + z = + let + z_aI5 = z1 + z2 + in + z_aI5 + in + z + in + foo_aIb + +After the simple optimizer runs, the Core program is: + + foo = 42 + 1 + +And the bindings for `z`, `z1`, and `z2` are all gone. If a plugin wanted to +analyse those bindings, it would have to deal with the unsimplified Core, but +cope with the generated bindings `z2_aHG`, `z1_aHR`, `z_aI5`, and `foo_aIb`, +all of which have no direct correspondence to the source code. + +Fortunately, a plugin can still improve the output by using the `so_inline` +field of `SimpleOpts`. The `so_inline` field is a /function/ of type +`(Id -> Bool)` that tells the simple optimiser whether or not to inline the `Id`. +The client of the GHC can thereby control precisely which bindings are inlined +and which are not. For instance, + + simplOptPgm + (defaultSimpleOpts { so_inline = (`notElem` ["z", "z1", "z2"]) }) + ... + +produces the following Core program: + + foo = + let + z2 = 1 + in + let + z1 = 42 + in + let + z = z1 + z2 + in + z + +which contains the bindings of interest and little else. + +For the specifics of how this affects a concrete plugin (Liquid Haskell), see +the discussion in https://gitlab.haskell.org/ghc/ghc/-/issues/24386 + +In addition to supporting clients of the GHC API, there is another use of +`so_inline` mentioned in 'simpleOptExprNoInline'. + -} -- | Simple optimiser options @@ -115,8 +202,11 @@ data SimpleOpts = SimpleOpts { so_uf_opts :: !UnfoldingOpts -- ^ Unfolding options , so_co_opts :: !OptCoercionOpts -- ^ Coercion optimiser options , so_eta_red :: !Bool -- ^ Eta reduction on? - , so_inline :: !Bool -- ^ False <=> do no inlining whatsoever, - -- even for trivial or used-once things + , so_inline :: !(Var -> Bool) -- ^ False <=> do no inline the given + -- binding whatsoever, even for trivial or + -- used-once things + -- + -- See Note [Controlling inlining in the simple optimiser] } -- | Default options for the Simple optimiser. @@ -125,7 +215,7 @@ defaultSimpleOpts = SimpleOpts { so_uf_opts = defaultUnfoldingOpts , so_co_opts = OptCoercionOpts { optCoercionEnabled = False } , so_eta_red = False - , so_inline = True + , so_inline = const True } simpleOptExpr :: HasDebugCallStack => SimpleOpts -> CoreExpr -> CoreExpr @@ -170,7 +260,7 @@ simpleOptExprNoInline :: HasDebugCallStack => SimpleOpts -> CoreExpr -> CoreExpr simpleOptExprNoInline opts expr = simple_opt_expr init_env expr where - init_opts = opts { so_inline = False } + init_opts = opts { so_inline = const False } init_env = (emptyEnv init_opts) { soe_subst = init_subst } init_subst = mkEmptySubst (mkInScopeSet (exprFreeVars expr)) @@ -639,12 +729,12 @@ simple_bind_pair env@(SOE { soe_inl = inl_env, soe_subst = subst, soe_opts = opt pre_inline_unconditionally :: Bool pre_inline_unconditionally - | not (so_inline opts) = False -- Not if so_inline is False - | isExportedId in_bndr = False - | stable_unf = False - | not active = False -- Note [Inline prag in simplOpt] - | not (safe_to_inline occ) = False - | otherwise = True + | not (so_inline opts in_bndr) = False -- Not if so_inline is False + | isExportedId in_bndr = False + | stable_unf = False + | not active = False -- Note [Inline prag in simplOpt] + | not (safe_to_inline occ) = False + | otherwise = True -- Unconditionally safe to inline safe_to_inline :: OccInfo -> Bool @@ -711,15 +801,15 @@ simple_out_bind_pair env@(SOE { soe_subst = subst, soe_opts = opts }) post_inline_unconditionally :: Bool post_inline_unconditionally - | not (so_inline opts) = False -- Not if so_inline is False - | isExportedId in_bndr = False -- Note [Exported Ids and trivial RHSs] - | stable_unf = False -- Note [Stable unfoldings and postInlineUnconditionally] - | not active = False -- in GHC.Core.Opt.Simplify.Utils - | is_loop_breaker = False -- If it's a loop-breaker of any kind, don't inline - -- because it might be referred to "earlier" - | exprIsTrivial out_rhs = True - | coercible_hack = True - | otherwise = False + | not (so_inline opts in_bndr) = False -- Not if so_inline is False + | isExportedId in_bndr = False -- Note [Exported Ids and trivial RHSs] + | stable_unf = False -- Note [Stable unfoldings and postInlineUnconditionally] + | not active = False -- in GHC.Core.Opt.Simplify.Utils + | is_loop_breaker = False -- If it's a loop-breaker of any kind, don't inline + -- because it might be referred to "earlier" + | exprIsTrivial out_rhs = True + | coercible_hack = True + | otherwise = False is_loop_breaker = isWeakLoopBreaker occ_info ===================================== compiler/GHC/Driver/Config.hs ===================================== @@ -26,7 +26,7 @@ initSimpleOpts dflags = SimpleOpts { so_uf_opts = unfoldingOpts dflags , so_co_opts = initOptCoercionOpts dflags , so_eta_red = gopt Opt_DoEtaReduction dflags - , so_inline = True + , so_inline = const True } -- | Instruct the interpreter evaluation to break... ===================================== libraries/base/src/Data/Functor/Classes.hs ===================================== @@ -85,7 +85,7 @@ import GHC.Internal.Read (expectP, list, paren, readField) import GHC.Internal.Show (appPrec) import GHC.Internal.Text.ParserCombinators.ReadPrec (ReadPrec, readPrec_to_S, readS_to_Prec, pfail) -import GHC.Internal.Text.Read (Read(..), parens, prec, step, reset) +import Text.Read (Read(..), parens, prec, step, reset) import GHC.Internal.Text.Read.Lex (Lexeme(..)) import GHC.Internal.Text.Show (showListWith) import Prelude ===================================== libraries/base/src/Data/Functor/Compose.hs ===================================== @@ -35,7 +35,7 @@ import GHC.Internal.Data.Foldable (Foldable(..)) import GHC.Internal.Data.Monoid (Sum(..), All(..), Any(..), Product(..)) import GHC.Internal.Data.Type.Equality (TestEquality(..), (:~:)(..)) import GHC.Generics (Generic, Generic1) -import GHC.Internal.Text.Read (Read(..), ReadPrec, readListDefault, readListPrecDefault) +import Text.Read (Read(..), ReadPrec, readListDefault, readListPrecDefault) import Prelude infixr 9 `Compose` ===================================== libraries/base/src/Prelude.hs ===================================== @@ -179,7 +179,7 @@ import GHC.Internal.Data.Tuple import GHC.Internal.Base hiding ( foldr, mapM, sequence ) import GHC.Internal.Classes import GHC.Internal.Err -import GHC.Internal.Text.Read +import Text.Read import GHC.Internal.Enum import GHC.Internal.Num import GHC.Internal.Prim (seq) ===================================== libraries/base/src/Text/Read.hs ===================================== @@ -39,5 +39,84 @@ module Text.Read readMaybe ) where -import GHC.Internal.Text.Read +import GHC.Err (errorWithoutStackTrace) +import GHC.Read + ( + ReadS, + Read (readsPrec, readList, readPrec, readListPrec), + lex, + readParen, + readListDefault, + lexP, + parens, + readListPrecDefault + ) +import Control.Monad (return) +import Data.Function (id) +import Data.Maybe (Maybe (Nothing, Just)) +import Data.Either (Either (Left, Right), either) +import Data.String (String) +import Text.Read.Lex (Lexeme (Char, String, Punc, Ident, Symbol, Number, EOF)) +import Text.ParserCombinators.ReadP (skipSpaces) import Text.ParserCombinators.ReadPrec + +-- $setup +-- >>> import Prelude + +------------------------------------------------------------------------ +-- utility functions + +-- | equivalent to 'readsPrec' with a precedence of 0. +reads :: Read a => ReadS a +reads = readsPrec minPrec + +-- | Parse a string using the 'Read' instance. +-- Succeeds if there is exactly one valid result. +-- A 'Left' value indicates a parse error. +-- +-- >>> readEither "123" :: Either String Int +-- Right 123 +-- +-- >>> readEither "hello" :: Either String Int +-- Left "Prelude.read: no parse" +-- +-- @since base-4.6.0.0 +readEither :: Read a => String -> Either String a +readEither s = + case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of + [x] -> Right x + [] -> Left "Prelude.read: no parse" + _ -> Left "Prelude.read: ambiguous parse" + where + read' = + do x <- readPrec + lift skipSpaces + return x + +-- | Parse a string using the 'Read' instance. +-- Succeeds if there is exactly one valid result. +-- +-- >>> readMaybe "123" :: Maybe Int +-- Just 123 +-- +-- >>> readMaybe "hello" :: Maybe Int +-- Nothing +-- +-- @since base-4.6.0.0 +readMaybe :: Read a => String -> Maybe a +readMaybe s = case readEither s of + Left _ -> Nothing + Right a -> Just a + +-- | The 'read' function reads input from a string, which must be +-- completely consumed by the input process. 'read' fails with an 'error' if the +-- parse is unsuccessful, and it is therefore discouraged from being used in +-- real applications. Use 'readMaybe' or 'readEither' for safe alternatives. +-- +-- >>> read "123" :: Int +-- 123 +-- +-- >>> read "hello" :: Int +-- *** Exception: Prelude.read: no parse +read :: Read a => String -> a +read s = either errorWithoutStackTrace id (readEither s) ===================================== libraries/ghc-internal/ghc-internal.cabal.in ===================================== @@ -329,7 +329,6 @@ Library GHC.Internal.System.Posix.Types GHC.Internal.Text.ParserCombinators.ReadP GHC.Internal.Text.ParserCombinators.ReadPrec - GHC.Internal.Text.Read GHC.Internal.Text.Read.Lex GHC.Internal.Text.Show GHC.Internal.Type.Reflection ===================================== libraries/ghc-internal/src/GHC/Internal/IO/Encoding.hs ===================================== @@ -46,7 +46,7 @@ import GHC.Internal.IO.Encoding.Types import qualified GHC.Internal.IO.Encoding.Iconv as Iconv #else import qualified GHC.Internal.IO.Encoding.CodePage as CodePage -import GHC.Internal.Text.Read (reads) +import GHC.Internal.Read (readsPrec) #endif import qualified GHC.Internal.IO.Encoding.Latin1 as Latin1 import qualified GHC.Internal.IO.Encoding.UTF8 as UTF8 @@ -319,7 +319,8 @@ mkTextEncoding' cfm enc = _ | isAscii -> return (Latin1.mkAscii cfm) _ | isLatin1 -> return (Latin1.mkLatin1_checked cfm) #if defined(mingw32_HOST_OS) - 'C':'P':n | [(cp,"")] <- reads n -> return $ CodePage.mkCodePageEncoding cfm cp + 'C':'P':n | [(cp,"")] <- readsPrec 0 n -> return $ CodePage.mkCodePageEncoding cfm cp + -- 'readsPrec 0' is the same as 'reads', but 'reads' is only defined in @base@. _ -> unknownEncodingErr (enc ++ codingFailureModeSuffix cfm) #else -- Otherwise, handle other encoding needs via iconv. ===================================== libraries/ghc-internal/src/GHC/Internal/Text/Read.hs deleted ===================================== @@ -1,115 +0,0 @@ -{-# LANGUAGE Trustworthy #-} -{-# LANGUAGE NoImplicitPrelude #-} - ------------------------------------------------------------------------------ --- | --- Module : GHC.Internal.Text.Read --- Copyright : (c) The University of Glasgow 2001 --- License : BSD-style (see the file libraries/base/LICENSE) --- --- Maintainer : libraries@haskell.org --- Stability : provisional --- Portability : non-portable (uses Text.ParserCombinators.ReadP) --- --- Converting strings to values. --- --- The "Text.Read" library is the canonical library to import for --- 'Read'-class facilities. For GHC only, it offers an extended and much --- improved 'Read' class, which constitutes a proposed alternative to the --- Haskell 2010 'Read'. In particular, writing parsers is easier, and --- the parsers are much more efficient. --- ------------------------------------------------------------------------------ - -module GHC.Internal.Text.Read ( - -- * The 'Read' class - Read(..), - ReadS, - - -- * Haskell 2010 functions - reads, - read, - readParen, - lex, - - -- * New parsing functions - module GHC.Internal.Text.ParserCombinators.ReadPrec, - L.Lexeme(..), - lexP, - parens, - readListDefault, - readListPrecDefault, - readEither, - readMaybe - - ) where - -import GHC.Internal.Base (String, id, return) -import GHC.Internal.Err (errorWithoutStackTrace) -import GHC.Internal.Maybe (Maybe(..)) -import GHC.Internal.Read -import GHC.Internal.Data.Either -import GHC.Internal.Text.ParserCombinators.ReadP as P -import GHC.Internal.Text.ParserCombinators.ReadPrec -import qualified GHC.Internal.Text.Read.Lex as L - --- $setup --- >>> import Prelude - ------------------------------------------------------------------------- --- utility functions - --- | equivalent to 'readsPrec' with a precedence of 0. -reads :: Read a => ReadS a -reads = readsPrec minPrec - --- | Parse a string using the 'Read' instance. --- Succeeds if there is exactly one valid result. --- A 'Left' value indicates a parse error. --- --- >>> readEither "123" :: Either String Int --- Right 123 --- --- >>> readEither "hello" :: Either String Int --- Left "Prelude.read: no parse" --- --- @since base-4.6.0.0 -readEither :: Read a => String -> Either String a -readEither s = - case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of - [x] -> Right x - [] -> Left "Prelude.read: no parse" - _ -> Left "Prelude.read: ambiguous parse" - where - read' = - do x <- readPrec - lift P.skipSpaces - return x - --- | Parse a string using the 'Read' instance. --- Succeeds if there is exactly one valid result. --- --- >>> readMaybe "123" :: Maybe Int --- Just 123 --- --- >>> readMaybe "hello" :: Maybe Int --- Nothing --- --- @since base-4.6.0.0 -readMaybe :: Read a => String -> Maybe a -readMaybe s = case readEither s of - Left _ -> Nothing - Right a -> Just a - --- | The 'read' function reads input from a string, which must be --- completely consumed by the input process. 'read' fails with an 'error' if the --- parse is unsuccessful, and it is therefore discouraged from being used in --- real applications. Use 'readMaybe' or 'readEither' for safe alternatives. --- --- >>> read "123" :: Int --- 123 --- --- >>> read "hello" :: Int --- *** Exception: Prelude.read: no parse -read :: Read a => String -> a -read s = either errorWithoutStackTrace id (readEither s) ===================================== testsuite/tests/ghc-api/T24386.hs ===================================== @@ -0,0 +1,123 @@ + +-- This test checks that bindings are preserved when configuring the simple +-- optimizer to not inline bindings with names selected by a predicate. +-- +-- This feature is important for the LiquidHaskell plugin, which relies on the +-- simple optimizer to make core programs easier to read, but needs to preserve +-- bindings that are relevant for verification. +-- +-- See https://gitlab.haskell.org/ghc/ghc/-/issues/24386 for the full discussion. +-- + +import Control.Monad +import Data.List (find) +import Data.Time (getCurrentTime) +import GHC +import GHC.Core +import GHC.Core.SimpleOpt +import GHC.Data.StringBuffer +import GHC.Driver.Config +import GHC.Driver.DynFlags +import GHC.Driver.Env.Types +import GHC.Types.Name +import GHC.Unit.Module.ModGuts +import GHC.Unit.Types +import GHC.Utils.Error +import GHC.Utils.Outputable + +import System.Environment (getArgs) + + +main :: IO () +main = + testLocalBindingsDesugaring + +testLocalBindingsDesugaring :: IO () +testLocalBindingsDesugaring = do + let inputSource = unlines + [ "module LocalBindingsDesugaring where" + , "f :: ()" + , "f = z" + , " where" + , " z = ()" + ] + + isExpectedDesugaring p = case findExpr "f" p of + Just (Let (NonRec b _) _) + -> isIdNamed "z" b + _ -> False + + isIdNamed name v = occNameString (occName v) == name + + coreProgram <- + compileToCore + (not . isIdNamed "z") + "LocalBindingsDesugaring" + inputSource + unless (isExpectedDesugaring coreProgram) $ + fail $ unlines $ + "Unexpected desugaring: No local binding for `z` found in the Core program." + : map showPprQualified coreProgram + +-- | Find the Core expression bound to the given name. +findExpr :: String -> CoreProgram -> Maybe CoreExpr +findExpr _ [] = + Nothing +findExpr name (p:ps) = case p of + NonRec b e + | occNameString (occName b) == name + -> Just e + Rec binds + | Just (_, e) <- find (\(b, _e) -> occNameString (occName b) == name) binds + -> Just e + _ -> findExpr name ps + +showPprQualified :: Outputable a => a -> String +showPprQualified = showSDocQualified . ppr + +showSDocQualified :: SDoc -> String +showSDocQualified = renderWithContext ctx + where + ctx = defaultSDocContext { sdocStyle = cmdlineParserStyle } + + + +compileToCore :: (Id -> Bool) -> String -> String -> IO [CoreBind] +compileToCore keepBindings modName inputSource = do + [libdir] <- getArgs + now <- getCurrentTime + runGhc (Just libdir) $ do + df1 <- getSessionDynFlags + GHC.setSessionDynFlags $ df1 { GHC.backend = GHC.bytecodeBackend } + let target = Target { + targetId = TargetFile (modName ++ ".hs") Nothing + , targetUnitId = homeUnitId_ df1 + , targetAllowObjCode = False + , targetContents = Just (stringToStringBuffer inputSource, now) + } + setTargets [target] + void $ GHC.depanal [] False + + dsMod <- getModSummary + (mkModule mainUnit (mkModuleName modName)) + >>= parseModule + >>= typecheckModule NoTcMPlugins + >>= desugarModule + hsc_env <- getSession + return $ mg_binds $ simpleOptimize keepBindings hsc_env $ dm_core_module dsMod + +-- Run the simple optimizer +simpleOptimize :: (Id -> Bool) -> GHC.HscEnv -> ModGuts -> ModGuts +simpleOptimize keepBindings hsc_env guts@(ModGuts + { mg_module = mgmod + , mg_binds = binds + , mg_rules = rules + }) = + let dflags = hsc_dflags hsc_env + simpl_opts = (initSimpleOpts dflags) { so_inline = keepBindings } + (binds2, rules2, _occ_anald_binds) = + simpleOptPgm simpl_opts mgmod binds rules + in guts + { mg_binds = binds2 + , mg_rules = rules2 + } ===================================== testsuite/tests/ghc-api/all.T ===================================== @@ -81,3 +81,4 @@ test('T26910', [ extra_run_opts(f'"{config.libdir}"') test('TypeMapStringLiteral', normal, compile_and_run, ['-package ghc']) test('T25121_status', normal, compile_and_run, ['-package ghc']) +test('T24386', [extra_run_opts(f'"{config.libdir}"')], compile_and_run, ['-package ghc']) ===================================== testsuite/tests/interface-stability/.gitignore ===================================== @@ -0,0 +1 @@ +download-base-exports ===================================== testsuite/tests/interface-stability/README.mkd ===================================== @@ -1,6 +1,6 @@ # Interface stability testing -The tests in this directory verify that the interfaces of exposed by GHC's +The tests in this directory verify that the interfaces exposed by GHC's core libraries do not inadvertently change. They use the `utils/dump-decls` utility to dump all exported declarations of all exposed modules for the following packages: @@ -27,7 +27,9 @@ The `base-exports` test in particular has rather platform-dependent output. Consequently, updating its output can be a bit tricky. There are two ways by which one can do this: - * Extrapolation: The various platforms' `base-exports.stdout` files are +#### Extrapolation + +The various platforms' `base-exports.stdout` files are similar enough that one can often apply the same patch of one file to the others. For instance: ``` @@ -40,8 +42,44 @@ which one can do this: In the case of conflicts, increasing the fuzz factor (using `-F`) can be quite effective. - * Using CI: Each CI job produces a tarball, `unexpected-test-output.tar.gz`, +#### Using CI + +Each CI job produces a tarball, `unexpected-test-output.tar.gz`, which contains the output produced by the job's failing tests. Simply - download this tarball and extracting the appropriate `base-exports.stdout-*` + download this tarball and extract the appropriate `base-exports.stdout-*` files into this directory. +Doing this by hand is of course very annoying. To make things faster, use the script in this folder called `download.base-exports.sh` : + +* Running for the first time + 1. Find the URL for downloading unexpected-test-output.tar.gz. To do so + * Go to the CI job page you want to download + * Click on "Browse" + * Find unexpected-test-output.tar.gz + * Right-click the download link then "Copy link" (Firefox) + 2. The URL should look like this : + `https://gitlab.haskell.org/ghc/ghc/-/jobs/2503744/artifacts/file/unexpected-test-output.tar.gz` + * the prefix is : `https://gitlab.haskell.org/ghc/ghc/-/jobs/` + * the job ID is : `2503744` + * and the suffix : `/artifacts/file/unexpected-test-output.tar.gz` + 3. The script prompts you with URL prefix and suffix. + 4. It will save a file to remember this, so you only need to do this once. + 5. If you need to change the URL, just edit the file `download-base-exports/url-unexpected-test-output` directly. + +* Downloading the artifacts + 1. Find all the job IDs you want to download. For this, just go to the jobs + page `https://gitlab.haskell.org/<YOUR-FORK>/ghc/-/jobs` + 2. Make sure you get all the artifacts. You need 3 of them. + To get all 3 CI jobs, the label `javascript` must be on the MR. + If you don't have the rights for adding these labels, ask. + 1. The `x86` CI job for darwin or linux : `base-exports.stdout` + 2. The `windows` job : `base-exports.stdout-mingw32` + 3. The `javascript` CI job : + `base-exports.stdout-javascript-unknown-ghcjs` + 3. Run the script with all the job IDs : + `./download-base-exports.sh 2502789 2502792 2502793` + + Using a range downloads more artifacts than necessary, but is a + no-brainer: + + `./download-base-exports.sh {2502789..2502795}` ===================================== testsuite/tests/interface-stability/download-base-exports.sh ===================================== @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# See the README file in this folder for usage + +jobIDs=("$@") + +BASE_DIR_NAME=download-base-exports +DL_DIR_NAME=dl +BASE_DIR="$(dirname "$0")/$BASE_DIR_NAME" +DL_DIR=$BASE_DIR/$DL_DIR_NAME +URL_FILE="$BASE_DIR/url-unexpected-test-output" + +DEFAULT_PREFIX="https://gitlab.haskell.org/ghc/ghc/-/jobs/" +DEFAULT_POSTFIX="/artifacts/raw/unexpected-test-output.tar.gz" + +mkdir -p "$BASE_DIR" + +# URL configuration for finding unexpected-test-output.tar.gz + +if [[ ! -f "$URL_FILE" ]]; then + echo "No URL for unexpected-test-output.tar.gz was found" + + read -p "Enter job URL prefix [${DEFAULT_PREFIX}]: " inputPrefix + read -p "Enter job URL postfix [${DEFAULT_POSTFIX}]: " inputPostfix + + urlPrefix="${inputPrefix:-$DEFAULT_PREFIX}" + urlPostfix="${inputPostfix:-$DEFAULT_POSTFIX}" + + { + echo "urlPrefix=$urlPrefix" + echo "urlPostfix=$urlPostfix" + } > "$URL_FILE" +else + source "$URL_FILE" +fi + +mkdir -p $DL_DIR + +echo "urlPrefix: $urlPrefix" +echo "jobIDs: $jobIDs" +echo "urlPostfix: $urlPostfix" +echo "" +echo "Downloading unexpected-test-output.tar.gz for each job ..." + +# Download and copy base-exports* files + +for jobID in "${jobIDs[@]}"; do + unexpectedOutputUrl="$urlPrefix$jobID$urlPostfix" + + wget -O "$DL_DIR/job$jobID.tar.gz" $unexpectedOutputUrl + + mkdir -p "$DL_DIR/job$jobID" + tar -xzf "$DL_DIR/job$jobID.tar.gz" -C "$DL_DIR/job$jobID" + cp "$DL_DIR/job$jobID"/unexpected-test-output/testsuite/tests/interface-stability/base-exports* "$BASE_DIR/.." +done ===================================== testsuite/tests/th/T24111.stdout ===================================== @@ -3,6 +3,6 @@ pattern (:+_0) :: GHC.Internal.Types.Int -> (GHC.Internal.Types.Int, GHC.Internal.Types.Int) pattern x_1 :+_0 y_2 = (x_1, y_2) pattern A_0 :: GHC.Internal.Types.Int -> GHC.Internal.Base.String -pattern A_0 n_1 <- (GHC.Internal.Text.Read.read -> n_1) where +pattern A_0 n_1 <- (Text.Read.read -> n_1) where A_0 0 = "hi" A_0 1 = "bye" ===================================== testsuite/tests/typecheck/should_compile/subsumption_sort_hole_fits.stderr ===================================== @@ -11,14 +11,13 @@ subsumption_sort_hole_fits.hs:2:5: warning: [GHC-88464] [-Wtyped-holes (in -Wdef words :: String -> [String] (imported from ‘Prelude’ (and originally defined in ‘GHC.Internal.Data.OldList’)) - read :: forall a. Read a => String -> a - with read @[String] - (imported from ‘Prelude’ - (and originally defined in ‘GHC.Internal.Text.Read’)) repeat :: forall a. a -> [a] with repeat @String (imported from ‘Prelude’ (and originally defined in ‘GHC.Internal.List’)) + read :: forall a. Read a => String -> a + with read @[String] + (imported from ‘Prelude’ (and originally defined in ‘Text.Read’)) mempty :: forall a. Monoid a => a with mempty @(String -> [String]) (imported from ‘Prelude’ ===================================== testsuite/tests/typecheck/should_fail/T21130.stderr ===================================== @@ -6,6 +6,9 @@ T21130.hs:10:6: error: [GHC-88464] In an equation for ‘x’: x = (_ f) :: Int • Relevant bindings include x :: Int (bound at T21130.hs:10:1) Valid hole fits include + read :: forall a. Read a => String -> a + with read @Int + (imported from ‘Prelude’ (and originally defined in ‘Text.Read’)) head :: forall a. GHC.Internal.Stack.Types.HasCallStack => [a] -> a with head @Int (imported from ‘Prelude’ @@ -14,10 +17,6 @@ T21130.hs:10:6: error: [GHC-88464] with last @Int (imported from ‘Prelude’ (and originally defined in ‘GHC.Internal.List’)) - read :: forall a. Read a => String -> a - with read @Int - (imported from ‘Prelude’ - (and originally defined in ‘GHC.Internal.Text.Read’)) T21130.hs:10:8: error: [GHC-39999] • Ambiguous type variable ‘t0’ arising from a use of ‘f’ View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/793302236da386b872e616859a7e78b... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/793302236da386b872e616859a7e78b... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Marge Bot (@marge-bot)