From 6998772043a7f0b0360116eb5ffcbaa5630b21fb Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" <ezyang@fb.com> Date: Sun, 29 Oct 2017 20:15:07 -0400 Subject: [PATCH] Make use of boot TyThings during typechecking. Summary: Suppose that you are typechecking A.hs, which transitively imports, via B.hs, A.hs-boot. When we poke on B.hs and discover that it has a reference to a type from A, what TyThing should we wire it up with? Clearly, if we have already typechecked A, we should use the most up-to-date TyThing: the one we freshly generated when we typechecked A. But what if we haven't typechecked it yet? For the longest time, GHC adopted the policy that this was *an error condition*; that you MUST NEVER poke on B.hs's reference to a thing defined in A.hs until A.hs has gotten around to checking this. However, actually ensuring this is the case has proven to be a bug farm. The problem was especially poignant with type family consistency checks, which eagerly happen before any typechecking takes place. This patch takes a different strategy: if we ever try to access an entity from A which doesn't exist, we just fall back on the definition of A from the hs-boot file. This means that you may end up with a mix of A.hs and A.hs-boot TyThings during the course of typechecking. Signed-off-by: Edward Z. Yang <ezyang@fb.com> Test Plan: validate Reviewers: simonpj, bgamari, austin, goldfire Subscribers: thomie, rwbarton GHC Trac Issues: #14396 Differential Revision: https://phabricator.haskell.org/D4154 --- compiler/iface/TcIface.hs | 65 +++++++--- compiler/rename/RnSource.hs | 120 ------------------ compiler/typecheck/FamInst.hs | 78 +++--------- compiler/typecheck/TcRnDriver.hs | 4 +- compiler/typecheck/TcRnMonad.hs | 1 - compiler/typecheck/TcRnTypes.hs | 7 - compiler/typecheck/TcTyClsDecls.hs | 4 - .../tests/typecheck/should_compile/T14396.hs | 4 + .../typecheck/should_compile/T14396.hs-boot | 2 + .../tests/typecheck/should_compile/T14396a.hs | 5 + .../tests/typecheck/should_compile/T14396b.hs | 4 + .../tests/typecheck/should_compile/T14396f.hs | 3 + .../tests/typecheck/should_compile/all.T | 1 + .../tests/typecheck/should_fail/T12042.stderr | 10 +- 14 files changed, 87 insertions(+), 221 deletions(-) create mode 100644 testsuite/tests/typecheck/should_compile/T14396.hs create mode 100644 testsuite/tests/typecheck/should_compile/T14396.hs-boot create mode 100644 testsuite/tests/typecheck/should_compile/T14396a.hs create mode 100644 testsuite/tests/typecheck/should_compile/T14396b.hs create mode 100644 testsuite/tests/typecheck/should_compile/T14396f.hs diff --git a/compiler/iface/TcIface.hs b/compiler/iface/TcIface.hs index b41c94823d6c..1a2d737726cd 100644 --- a/compiler/iface/TcIface.hs +++ b/compiler/iface/TcIface.hs @@ -1714,13 +1714,13 @@ tcIfaceGlobal name { type_env <- setLclEnv () get_type_env -- yuk ; case lookupNameEnv type_env name of Just thing -> return thing - Nothing -> - pprPanic "tcIfaceGlobal (local): not found" - (ifKnotErr name (if_doc env) type_env) + -- See Note [Knot-tying fallback on boot] + Nothing -> via_external } - ; _ -> do - + ; _ -> via_external }} + where + via_external = do { hsc_env <- getTopEnv ; mb_thing <- liftIO (lookupTypeHscEnv hsc_env name) ; case mb_thing of { @@ -1731,21 +1731,7 @@ tcIfaceGlobal name ; case mb_thing of Failed err -> failIfM err Succeeded thing -> return thing - }}}}} - -ifKnotErr :: Name -> SDoc -> TypeEnv -> SDoc -ifKnotErr name env_doc type_env = vcat - [ text "You are in a maze of twisty little passages, all alike." - , text "While forcing the thunk for TyThing" <+> ppr name - , text "which was lazily initialized by" <+> env_doc <> text "," - , text "I tried to tie the knot, but I couldn't find" <+> ppr name - , text "in the current type environment." - , text "If you are developing GHC, please read Note [Tying the knot]" - , text "and Note [Type-checking inside the knot]." - , text "Consider rebuilding GHC with profiling for a better stack trace." - , hang (text "Contents of current type environment:") - 2 (ppr type_env) - ] + }}} -- Note [Tying the knot] -- ~~~~~~~~~~~~~~~~~~~~~ @@ -1760,11 +1746,50 @@ ifKnotErr name env_doc type_env = vcat -- * Note [Knot-tying typecheckIface] -- * Note [DFun knot-tying] -- * Note [hsc_type_env_var hack] +-- * Note [Knot-tying fallback on boot] -- -- There is also a wiki page on the subject, see: -- -- https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/TyingTheKnot +-- Note [Knot-tying fallback on boot] +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-- Suppose that you are typechecking A.hs, which transitively imports, +-- via B.hs, A.hs-boot. When we poke on B.hs and discover that it +-- has a reference to a type T from A, what TyThing should we wire +-- it up with? Clearly, if we have already typechecked T and +-- added it into the type environment, we should go ahead and use that +-- type. But what if we haven't typechecked it yet? +-- +-- For the longest time, GHC adopted the policy that this was +-- *an error condition*; that you MUST NEVER poke on B.hs's reference +-- to a T defined in A.hs until A.hs has gotten around to kind-checking +-- T and adding it to the env. However, actually ensuring this is the +-- case has proven to be a bug farm, because it's really difficult to +-- actually ensure this never happens. The problem was especially poignant +-- with type family consistency checks, which eagerly happen before any +-- typechecking takes place. +-- +-- Today, we take a different strategy: if we ever try to access +-- an entity from A which doesn't exist, we just fall back on the +-- definition of A from the hs-boot file. This is complicated in +-- its own way: it means that you may end up with a mix of A.hs and +-- A.hs-boot TyThings during the course of typechecking. We don't +-- think (and have not observed) any cases where this would cause +-- problems, but the hypothetical situation one might worry about +-- is something along these lines in Core: +-- +-- case x of +-- A -> e1 +-- B -> e2 +-- +-- If, when typechecking this, we find x :: T, and the T we are hooked +-- up with is the abstract one from the hs-boot file, rather than the +-- one defined in this module with constructors A and B. But it's hard +-- to see how this could happen, especially because the reference to +-- the constructor (A and B) means that GHC will always typecheck +-- this expression *after* typechecking T. + tcIfaceTyConByName :: IfExtName -> IfL TyCon tcIfaceTyConByName name = do { thing <- tcIfaceGlobal name diff --git a/compiler/rename/RnSource.hs b/compiler/rename/RnSource.hs index b182382381bb..c0347c4d6b6c 100644 --- a/compiler/rename/RnSource.hs +++ b/compiler/rename/RnSource.hs @@ -1283,9 +1283,6 @@ rnTyClDecls tycl_ds ; instds_w_fvs <- mapM (wrapLocFstM rnSrcInstDecl) (tyClGroupInstDecls tycl_ds) ; role_annots <- rnRoleAnnots tc_names (tyClGroupRoleDecls tycl_ds) - ; tycls_w_fvs <- addBootDeps tycls_w_fvs - -- TBD must add_boot_deps to instds_w_fvs? - -- Do SCC analysis on the type/class decls ; rdr_env <- getGlobalRdrEnv ; let tycl_sccs = depAnalTyClDecls rdr_env tycls_w_fvs @@ -1365,123 +1362,6 @@ getParent rdr_env n Nothing -> n -{- Note [Extra dependencies from .hs-boot files] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is a long story, so buckle in. - -**Dependencies via hs-boot files are not obvious.** Consider the following case: - -A.hs-boot - module A where - data A1 - -B.hs - module B where - import {-# SOURCE #-} A - type B1 = A1 - -A.hs - module A where - import B - data A2 = MkA2 B1 - data A1 = MkA1 A2 - -Here A2 is really recursive (via B1), but we won't see that easily when -doing dependency analysis when compiling A.hs. When we look at A2, -we see that its free variables are simply B1, but without (recursively) digging -into the definition of B1 will we see that it actually refers to A1 via an -hs-boot file. - -**Recursive declarations, even those broken by an hs-boot file, need to -be type-checked together.** Whenever we refer to a declaration via -an hs-boot file, we must be careful not to force the TyThing too early: -ala Note [Tying the knot] if we force the TyThing before we have -defined it ourselves in the local type environment, GHC will error. - -Conservatively, then, it would make sense that we to typecheck A1 -and A2 from the previous example together, because the two types are -truly mutually recursive through B1. - -If we are being clever, we might observe that while kind-checking -A2, we don't actually need to force the TyThing for A1: B1 -independently records its kind, so there is no need to go "deeper". -But then we are in an uncomfortable situation where we have -constructed a TyThing for A2 before we have checked A1, and we -have to be absolutely certain we don't force it too deeply until -we get around to kind checking A1, which could be for a very long -time. - -Indeed, with datatype promotion, we may very well need to look -at the type of MkA2 before we have kind-checked A1: consider, - - data T = MkT (Proxy 'MkA2) - -To promote MkA2, we need to lift its type to the kind level. -We never tested this, but it seems likely A1 would get poked -at this point. - -**Here's what we do instead.** So it is expedient for us to -make sure A1 and A2 are kind checked together in a loop. -To ensure that our dependency analysis can catch this, -we add a dependency: - - - from every local declaration - - to everything that comes from this module's .hs-boot file - (this is gotten from sb_tcs in the SelfBootInfo). - -In this case, we'll add an edges - - - from A1 to A2 (but that edge is there already) - - from A2 to A1 (which is new) - -Well, not quite *every* declaration. Imagine module A -above had another datatype declaration: - - data A3 = A3 Int - -Even though A3 has a dependency (on Int), all its dependencies are from things -that live on other packages. Since we don't have mutual dependencies across -packages, it is safe not to add the dependencies on the .hs-boot stuff to A2. - -Hence function nameIsHomePackageImport. - -Note that this is fairly conservative: it essentially implies that -EVERY type declaration in this modules hs-boot file will be kind-checked -together in one giant loop (and furthermore makes every other type -in the module depend on this loop). This is perhaps less than ideal, because -the larger a recursive group, the less polymorphism available (we -cannot infer a type to be polymorphically instantiated while we -are inferring its kind), but no one has hollered about this (yet!) --} - -addBootDeps :: [(LTyClDecl GhcRn, FreeVars)] - -> RnM [(LTyClDecl GhcRn, FreeVars)] --- See Note [Extra dependencies from .hs-boot files] -addBootDeps ds_w_fvs - = do { tcg_env <- getGblEnv - ; let this_mod = tcg_mod tcg_env - boot_info = tcg_self_boot tcg_env - - add_boot_deps :: [(LTyClDecl GhcRn, FreeVars)] - -> [(LTyClDecl GhcRn, FreeVars)] - add_boot_deps ds_w_fvs - = case boot_info of - SelfBoot { sb_tcs = tcs } | not (isEmptyNameSet tcs) - -> map (add_one tcs) ds_w_fvs - _ -> ds_w_fvs - - add_one :: NameSet -> (LTyClDecl GhcRn, FreeVars) - -> (LTyClDecl GhcRn, FreeVars) - add_one tcs pr@(decl,fvs) - | has_local_imports fvs = (decl, fvs `plusFV` tcs) - | otherwise = pr - - has_local_imports fvs - = nameSetAny (nameIsHomePackageImport this_mod) fvs - ; return (add_boot_deps ds_w_fvs) } - - - {- ****************************************************** * * Role annotations diff --git a/compiler/typecheck/FamInst.hs b/compiler/typecheck/FamInst.hs index 9c91daafc4a9..94bc43f862ad 100644 --- a/compiler/typecheck/FamInst.hs +++ b/compiler/typecheck/FamInst.hs @@ -7,7 +7,6 @@ module FamInst ( checkFamInstConsistency, tcExtendLocalFamInstEnv, tcLookupDataFamInst, tcLookupDataFamInst_maybe, tcInstNewTyCon_maybe, tcTopNormaliseNewTypeTF_maybe, - checkRecFamInstConsistency, newFamInst, -- * Injectivity @@ -43,8 +42,6 @@ import Panic import VarSet import Bag( Bag, unionBags, unitBag ) import Control.Monad -import NameEnv -import Data.List #include "HsVersions.h" @@ -102,8 +99,7 @@ defined in the module M itself. This is a pairwise check, i.e., for every pair of instances we must check that they are consistent. - For family instances coming from `dep_finsts`, this is checked in -checkFamInstConsistency, called from tcRnImports, and in -checkRecFamInstConsistency, called from tcTyClGroup. See Note +checkFamInstConsistency, called from tcRnImports. See Note [Checking family instance consistency] for details on this check (and in particular how we avoid having to do all these checks for every module we compile). @@ -276,16 +272,14 @@ This is basically the idea from #13092, comment:14. -- This function doesn't check ALL instances for consistency, -- only ones that aren't involved in recursive knot-tying -- loops; see Note [Don't check hs-boot type family instances too early]. --- It returns a modified 'TcGblEnv' that has saved the --- instances that need to be checked later; use 'checkRecFamInstConsistency' --- to check those. -- We don't need to check the current module, this is done in -- tcExtendLocalFamInstEnv. -- See Note [The type family instance consistency story]. -checkFamInstConsistency :: [Module] -> TcM TcGblEnv +checkFamInstConsistency :: [Module] -> TcM () checkFamInstConsistency directlyImpMods = do { dflags <- getDynFlags ; (eps, hpt) <- getEpsAndHpt + ; traceTc "checkFamInstConsistency" (ppr directlyImpMods) ; let { -- Fetch the iface of a given module. Must succeed as -- all directly imported modules must already have been loaded. modIface mod = @@ -313,10 +307,7 @@ checkFamInstConsistency directlyImpMods } - ; pending_checks <- checkMany hpt_fam_insts modConsistent directlyImpMods - ; tcg_env <- getGblEnv - ; return tcg_env { tcg_pending_fam_checks - = foldl' (plusNameEnv_C (++)) emptyNameEnv pending_checks } + ; checkMany hpt_fam_insts modConsistent directlyImpMods } where -- See Note [Checking family instance optimization] @@ -324,26 +315,24 @@ checkFamInstConsistency directlyImpMods :: ModuleEnv FamInstEnv -- home package family instances -> (Module -> [Module]) -- given A, modules checked when A was checked -> [Module] -- modules to process - -> TcM [NameEnv [([FamInst], FamInstEnv)]] - checkMany hpt_fam_insts modConsistent mods = go [] emptyModuleSet mods [] + -> TcM () + checkMany hpt_fam_insts modConsistent mods = go [] emptyModuleSet mods where go :: [Module] -- list of consistent modules -> ModuleSet -- set of consistent modules, same elements as the -- list above -> [Module] -- modules to process - -> [NameEnv [([FamInst], FamInstEnv)]] - -- accumulator for pending checks - -> TcM [NameEnv [([FamInst], FamInstEnv)]] - go _ _ [] pending = return pending - go consistent consistent_set (mod:mods) pending = do - pending' <- sequence + -> TcM () + go _ _ [] = return () + go consistent consistent_set (mod:mods) = do + sequence_ [ check hpt_fam_insts m1 m2 | m1 <- to_check_from_mod -- loop over toCheckFromMod first, it's usually smaller, -- it may even be empty , m2 <- to_check_from_consistent ] - go consistent' consistent_set' mods (pending' ++ pending) + go consistent' consistent_set' mods where mod_deps_consistent = modConsistent mod mod_deps_consistent_set = mkModuleSet mod_deps_consistent @@ -358,10 +347,7 @@ checkFamInstConsistency directlyImpMods -- We could, but doing so means one of two things: -- -- 1. When looping over the cartesian product we convert - -- a set into a non-deterministicly ordered list - then - -- tcg_pending_fam_checks will end up storing some - -- non-deterministically ordered lists as well and - -- we end up with non-local non-determinism. Which + -- a set into a non-deterministicly ordered list. Which -- happens to be fine for interface file determinism -- in this case, today, because the order only -- determines the order of deferred checks. But such @@ -434,12 +420,9 @@ checkFamInstConsistency directlyImpMods -- import B -- data T = MkT -- - -- However, this is not yet done; see #13981. - -- - -- Note that it is NOT necessary to defer for occurrences in the - -- RHS (e.g., type instance F Int = T, in the above example), - -- since that never participates in consistency checking - -- in any nontrivial way. + -- In fact, it is even necessary to defer for occurrences in + -- the RHS, because we may test for *compatibility* in event + -- of an overlap. -- -- Why don't we defer ALL of the checks to later? Well, many -- instances aren't involved in the recursive loop at all. So @@ -453,40 +436,11 @@ checkFamInstConsistency directlyImpMods -- -- See also Note [Tying the knot] and Note [Type-checking inside the knot] -- for why we are doing this at all. - ; this_mod <- getModule - -- NB: == this_mod only holds if there's an hs-boot file; - -- otherwise we cannot possible see instances for families - -- defined by the module we are compiling in imports. - ; let shouldCheckNow = ((/= this_mod) . nameModule . fi_fam) - (check_now, check_later) = - partition shouldCheckNow (famInstEnvElts env1) + ; let check_now = famInstEnvElts env1 ; mapM_ (checkForConflicts (emptyFamInstEnv, env2)) check_now ; mapM_ (checkForInjectivityConflicts (emptyFamInstEnv,env2)) check_now - ; let check_later_map = - extendNameEnvList_C (++) emptyNameEnv - [(fi_fam finst, [finst]) | finst <- check_later] - ; return (mapNameEnv (\xs -> [(xs, env2)]) check_later_map) } --- | Given a 'TyCon' that has been incorporated into the type --- environment (the knot is tied), if it is a type family, check --- that all deferred instances for it are consistent. --- See Note [Don't check hs-boot type family instances too early] -checkRecFamInstConsistency :: TyCon -> TcM () -checkRecFamInstConsistency tc = do - tcg_env <- getGblEnv - let checkConsistency tc - | isFamilyTyCon tc - , Just pairs <- lookupNameEnv (tcg_pending_fam_checks tcg_env) - (tyConName tc) - = forM_ pairs $ \(check_now, env2) -> do - mapM_ (checkForConflicts (emptyFamInstEnv, env2)) check_now - mapM_ (checkForInjectivityConflicts (emptyFamInstEnv,env2)) check_now - | otherwise - = return () - checkConsistency tc - - getFamInsts :: ModuleEnv FamInstEnv -> Module -> TcM FamInstEnv getFamInsts hpt_fam_insts mod | Just env <- lookupModuleEnv hpt_fam_insts mod = return env diff --git a/compiler/typecheck/TcRnDriver.hs b/compiler/typecheck/TcRnDriver.hs index fd63effbe638..40b5efec8429 100644 --- a/compiler/typecheck/TcRnDriver.hs +++ b/compiler/typecheck/TcRnDriver.hs @@ -370,10 +370,10 @@ tcRnImports hsc_env import_decls ; let { dir_imp_mods = moduleEnvKeys . imp_mods $ imports } - ; tcg_env <- checkFamInstConsistency dir_imp_mods ; + ; checkFamInstConsistency dir_imp_mods ; traceRn "rn1: } checking family instance consistency" empty - ; return tcg_env } } + ; getGblEnv } } {- ************************************************************************ diff --git a/compiler/typecheck/TcRnMonad.hs b/compiler/typecheck/TcRnMonad.hs index 5121fb571f50..5bc200c1a057 100644 --- a/compiler/typecheck/TcRnMonad.hs +++ b/compiler/typecheck/TcRnMonad.hs @@ -258,7 +258,6 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this tcg_type_env_var = type_env_var, tcg_inst_env = emptyInstEnv, tcg_fam_inst_env = emptyFamInstEnv, - tcg_pending_fam_checks = emptyNameEnv, tcg_ann_env = emptyAnnEnv, tcg_th_used = th_var, tcg_th_splice_used = th_splice_var, diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs index 5f7498fa163e..5f14b455ad74 100644 --- a/compiler/typecheck/TcRnTypes.hs +++ b/compiler/typecheck/TcRnTypes.hs @@ -527,13 +527,6 @@ data TcGblEnv tcg_fam_inst_env :: FamInstEnv, -- ^ Ditto for family instances tcg_ann_env :: AnnEnv, -- ^ And for annotations - -- | Family instances we have to check for consistency. - -- Invariant: each FamInst in the list's fi_fam matches the - -- key of the entry in the 'NameEnv'. This gets consumed - -- by 'checkRecFamInstConsistency'. - -- See Note [Don't check hs-boot type family instances too early] - tcg_pending_fam_checks :: NameEnv [([FamInst], FamInstEnv)], - -- Now a bunch of things about this module that are simply -- accumulated, but never consulted until the end. -- Nevertheless, it's convenient to accumulate them along diff --git a/compiler/typecheck/TcTyClsDecls.hs b/compiler/typecheck/TcTyClsDecls.hs index b6fe855efacb..f77a70b69ba5 100644 --- a/compiler/typecheck/TcTyClsDecls.hs +++ b/compiler/typecheck/TcTyClsDecls.hs @@ -162,10 +162,6 @@ tcTyClGroup (TyClGroup { group_tyclds = tyclds ; checkSynCycles this_uid tyclss tyclds ; traceTc "Done synonym cycle check" (ppr tyclss) - ; traceTc "Starting family consistency check" (ppr tyclss) - ; forM_ tyclss checkRecFamInstConsistency - ; traceTc "Done family consistency" (ppr tyclss) - -- Step 2: Perform the validity check on those types/classes -- We can do this now because we are done with the recursive knot -- Do it before Step 3 (adding implicit things) because the latter diff --git a/testsuite/tests/typecheck/should_compile/T14396.hs b/testsuite/tests/typecheck/should_compile/T14396.hs new file mode 100644 index 000000000000..2d6c7061c309 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T14396.hs @@ -0,0 +1,4 @@ +module T14396 where +import T14396a +import T14396b +data S = S diff --git a/testsuite/tests/typecheck/should_compile/T14396.hs-boot b/testsuite/tests/typecheck/should_compile/T14396.hs-boot new file mode 100644 index 000000000000..d4fe8673c36a --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T14396.hs-boot @@ -0,0 +1,2 @@ +module T14396 where +data S = S diff --git a/testsuite/tests/typecheck/should_compile/T14396a.hs b/testsuite/tests/typecheck/should_compile/T14396a.hs new file mode 100644 index 000000000000..28a276c7195c --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T14396a.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE TypeFamilies #-} +module T14396a where +import T14396f +import {-# SOURCE #-} T14396(S) +type instance F Int = S diff --git a/testsuite/tests/typecheck/should_compile/T14396b.hs b/testsuite/tests/typecheck/should_compile/T14396b.hs new file mode 100644 index 000000000000..aabf3f3dcccb --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T14396b.hs @@ -0,0 +1,4 @@ +{-# LANGUAGE TypeFamilies #-} +module T14396b where +import T14396f +type instance F Bool = Int diff --git a/testsuite/tests/typecheck/should_compile/T14396f.hs b/testsuite/tests/typecheck/should_compile/T14396f.hs new file mode 100644 index 000000000000..e6230fd11b42 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T14396f.hs @@ -0,0 +1,3 @@ +{-# LANGUAGE TypeFamilies #-} +module T14396f where +type family F a diff --git a/testsuite/tests/typecheck/should_compile/all.T b/testsuite/tests/typecheck/should_compile/all.T index 19a99db6290a..3c6ef7fb5a48 100644 --- a/testsuite/tests/typecheck/should_compile/all.T +++ b/testsuite/tests/typecheck/should_compile/all.T @@ -581,6 +581,7 @@ test('T14333', normal, compile, ['']) test('T14363', normal, compile, ['']) test('T14363a', normal, compile, ['']) test('T7169', normal, compile, ['']) +test('T14396', [extra_files(['T14396.hs', 'T14396.hs-boot', 'T14396a.hs', 'T14396b.hs', 'T14396f.hs'])], multimod_compile, ['T14396', '-v0']) test('T14434', [], run_command, ['$MAKE -s --no-print-directory T14434']) test('MissingExportList01', normal, compile, ['']) test('MissingExportList02', normal, compile, ['']) diff --git a/testsuite/tests/typecheck/should_fail/T12042.stderr b/testsuite/tests/typecheck/should_fail/T12042.stderr index e9b139ca5775..3266a1fe11a4 100644 --- a/testsuite/tests/typecheck/should_fail/T12042.stderr +++ b/testsuite/tests/typecheck/should_fail/T12042.stderr @@ -2,8 +2,8 @@ [2 of 3] Compiling T12042a ( T12042a.hs, T12042a.o ) [3 of 3] Compiling T12042 ( T12042.hs, T12042.o ) -T12042.hs:3:1: error: - Cycle in type synonym declarations: - T12042.hs:3:1-12: type S = R - T12042a.hs:3:1-10: B.U from external module - T12042.hs:4:1-12: type R = B.U +T12042.hs-boot:2:1: error: + Type constructor ‘S’ has conflicting definitions in the module + and its hs-boot file + Main module: type S = R + Boot file: data S -- GitLab