diff options
-rw-r--r-- | compiler/GHC/Tc/Deriv.hs | 4 | ||||
-rw-r--r-- | compiler/GHC/Tc/Deriv/Generate.hs | 593 | ||||
-rw-r--r-- | compiler/GHC/Tc/Deriv/Utils.hs | 7 | ||||
-rw-r--r-- | compiler/GHC/Types/Name/Occurrence.hs | 20 | ||||
-rw-r--r-- | testsuite/tests/deriving/should_compile/T14682.stderr | 5 | ||||
-rw-r--r-- | testsuite/tests/deriving/should_compile/T18321.hs | 15 | ||||
-rw-r--r-- | testsuite/tests/deriving/should_compile/all.T | 1 |
7 files changed, 370 insertions, 275 deletions
diff --git a/compiler/GHC/Tc/Deriv.hs b/compiler/GHC/Tc/Deriv.hs index a4d2be7ac6..013fd76a3e 100644 --- a/compiler/GHC/Tc/Deriv.hs +++ b/compiler/GHC/Tc/Deriv.hs @@ -202,9 +202,7 @@ tcDeriving deriv_infos deriv_decls ; dflags <- getDynFlags ; let (_, deriv_stuff, fvs) = unzip3 (insts1 ++ insts2) - ; loc <- getSrcSpanM - ; let (binds, famInsts) = genAuxBinds dflags loc - (unionManyBags deriv_stuff) + ; let (binds, famInsts) = genAuxBinds (unionManyBags deriv_stuff) ; let mk_inst_infos1 = map fstOf3 insts1 ; inst_infos1 <- apply_inst_infos mk_inst_infos1 given_specs diff --git a/compiler/GHC/Tc/Deriv/Generate.hs b/compiler/GHC/Tc/Deriv/Generate.hs index a9791043a2..2ebd1d11eb 100644 --- a/compiler/GHC/Tc/Deriv/Generate.hs +++ b/compiler/GHC/Tc/Deriv/Generate.hs @@ -46,8 +46,6 @@ import GHC.Types.Name.Reader import GHC.Types.Basic import GHC.Core.DataCon import GHC.Types.Name -import GHC.Utils.Fingerprint -import GHC.Utils.Encoding import GHC.Driver.Session import GHC.Builtin.Utils @@ -81,22 +79,18 @@ import Data.List ( find, partition, intersperse ) type BagDerivStuff = Bag DerivStuff -data AuxBindSpec - = DerivCon2Tag TyCon -- The con2Tag for given TyCon - | DerivTag2Con TyCon -- ...ditto tag2Con - | DerivMaxTag TyCon -- ...and maxTag - deriving( Eq ) - -- All these generate ZERO-BASED tag operations - -- I.e first constructor has tag 0 - data DerivStuff -- Please add this auxiliary stuff - = DerivAuxBind AuxBindSpec - - -- Generics and DeriveAnyClass - | DerivFamInst FamInst -- New type family instances + = DerivFamInst FamInst + -- ^ A new type family instance. Used for: + -- + -- * @DeriveGeneric@, which generates instances of @Rep(1)@ + -- + -- * @DeriveAnyClass@, which can fill in associated type family defaults -- New top-level auxiliary bindings - | DerivHsBind (LHsBind GhcPs, LSig GhcPs) -- Also used for SYB + | DerivHsBind (LHsBind GhcPs, LSig GhcPs) + -- ^ A new, top-level auxiliary binding. Used for deriving 'Eq', 'Ord', + -- 'Enum', 'Ix', and 'Data'. See Note [Auxiliary binders]. {- @@ -160,8 +154,11 @@ produced don't get through the typechecker. gen_Eq_binds :: SrcSpan -> TyCon -> TcM (LHsBinds GhcPs, BagDerivStuff) gen_Eq_binds loc tycon = do - dflags <- getDynFlags - return (method_binds dflags, aux_binds) + -- See Note [Auxiliary binders] + con2tag_occ <- chooseUniqueOccTc (mkCon2TagOcc (getOccName tycon)) + let con2tag_RDR = mkRdrUnqual con2tag_occ + + return (method_binds con2tag_RDR, aux_binds con2tag_RDR) where all_cons = tyConDataCons tycon (nullary_cons, non_nullary_cons) = partition isNullarySrcDataCon all_cons @@ -175,7 +172,7 @@ gen_Eq_binds loc tycon = do no_tag_match_cons = null tag_match_cons - fall_through_eqn dflags + fall_through_eqn con2tag_RDR | no_tag_match_cons -- All constructors have arguments = case pat_match_cons of [] -> [] -- No constructors; no fall-though case @@ -187,16 +184,18 @@ gen_Eq_binds loc tycon = do | otherwise -- One or more tag_match cons; add fall-through of -- extract tags compare for equality = [([a_Pat, b_Pat], - untag_Expr dflags tycon [(a_RDR,ah_RDR), (b_RDR,bh_RDR)] + untag_Expr con2tag_RDR [(a_RDR,ah_RDR), (b_RDR,bh_RDR)] (genPrimOpApp (nlHsVar ah_RDR) eqInt_RDR (nlHsVar bh_RDR)))] - aux_binds | no_tag_match_cons = emptyBag - | otherwise = unitBag $ DerivAuxBind $ DerivCon2Tag tycon + aux_binds con2tag_RDR + | no_tag_match_cons = emptyBag + | otherwise = unitBag $ genCon2Tag con2tag_RDR loc tycon - method_binds dflags = unitBag (eq_bind dflags) - eq_bind dflags = mkFunBindEC 2 loc eq_RDR (const true_Expr) - (map pats_etc pat_match_cons - ++ fall_through_eqn dflags) + method_binds con2tag_RDR = unitBag (eq_bind con2tag_RDR) + eq_bind con2tag_RDR + = mkFunBindEC 2 loc eq_RDR (const true_Expr) + (map pats_etc pat_match_cons + ++ fall_through_eqn con2tag_RDR) ------------------------------------------------------------------ pats_etc data_con @@ -340,21 +339,26 @@ gtResult OrdGT = true_Expr ------------ gen_Ord_binds :: SrcSpan -> TyCon -> TcM (LHsBinds GhcPs, BagDerivStuff) gen_Ord_binds loc tycon = do - dflags <- getDynFlags + -- See Note [Auxiliary binders] + con2tag_occ <- chooseUniqueOccTc (mkCon2TagOcc (getOccName tycon)) + let con2tag_RDR = mkRdrUnqual con2tag_occ + return $ if null tycon_data_cons -- No data-cons => invoke bale-out case then ( unitBag $ mkFunBindEC 2 loc compare_RDR (const eqTag_Expr) [] , emptyBag) - else ( unitBag (mkOrdOp dflags OrdCompare) `unionBags` other_ops dflags - , aux_binds) + else ( unitBag (mkOrdOp con2tag_RDR OrdCompare) + `unionBags` other_ops con2tag_RDR + , aux_binds con2tag_RDR) where - aux_binds | single_con_type = emptyBag - | otherwise = unitBag $ DerivAuxBind $ DerivCon2Tag tycon + aux_binds con2tag_RDR + | single_con_type = emptyBag + | otherwise = unitBag $ genCon2Tag con2tag_RDR loc tycon -- Note [Game plan for deriving Ord] - other_ops dflags + other_ops con2tag_RDR | (last_tag - first_tag) <= 2 -- 1-3 constructors || null non_nullary_cons -- Or it's an enumeration - = listToBag [mkOrdOp dflags OrdLT, lE, gT, gE] + = listToBag [mkOrdOp con2tag_RDR OrdLT, lE, gT, gE] | otherwise = emptyBag @@ -380,39 +384,40 @@ gen_Ord_binds loc tycon = do (nullary_cons, non_nullary_cons) = partition isNullarySrcDataCon tycon_data_cons - mkOrdOp :: DynFlags -> OrdOp -> LHsBind GhcPs + mkOrdOp :: RdrName -> OrdOp -> LHsBind GhcPs -- Returns a binding op a b = ... compares a and b according to op .... - mkOrdOp dflags op = mkSimpleGeneratedFunBind loc (ordMethRdr op) [a_Pat, b_Pat] - (mkOrdOpRhs dflags op) + mkOrdOp con2tag_RDR op + = mkSimpleGeneratedFunBind loc (ordMethRdr op) [a_Pat, b_Pat] + (mkOrdOpRhs con2tag_RDR op) - mkOrdOpRhs :: DynFlags -> OrdOp -> LHsExpr GhcPs - mkOrdOpRhs dflags op -- RHS for comparing 'a' and 'b' according to op + mkOrdOpRhs :: RdrName -> OrdOp -> LHsExpr GhcPs + mkOrdOpRhs con2tag_RDR op -- RHS for comparing 'a' and 'b' according to op | nullary_cons `lengthAtMost` 2 -- Two nullary or fewer, so use cases = nlHsCase (nlHsVar a_RDR) $ - map (mkOrdOpAlt dflags op) tycon_data_cons + map (mkOrdOpAlt con2tag_RDR op) tycon_data_cons -- i.e. case a of { C1 x y -> case b of C1 x y -> ....compare x,y... -- C2 x -> case b of C2 x -> ....comopare x.... } | null non_nullary_cons -- All nullary, so go straight to comparing tags - = mkTagCmp dflags op + = mkTagCmp con2tag_RDR op | otherwise -- Mixed nullary and non-nullary = nlHsCase (nlHsVar a_RDR) $ - (map (mkOrdOpAlt dflags op) non_nullary_cons - ++ [mkHsCaseAlt nlWildPat (mkTagCmp dflags op)]) + (map (mkOrdOpAlt con2tag_RDR op) non_nullary_cons + ++ [mkHsCaseAlt nlWildPat (mkTagCmp con2tag_RDR op)]) - mkOrdOpAlt :: DynFlags -> OrdOp -> DataCon + mkOrdOpAlt :: RdrName -> OrdOp -> DataCon -> LMatch GhcPs (LHsExpr GhcPs) -- Make the alternative (Ki a1 a2 .. av -> - mkOrdOpAlt dflags op data_con + mkOrdOpAlt con2tag_RDR op data_con = mkHsCaseAlt (nlConVarPat data_con_RDR as_needed) - (mkInnerRhs dflags op data_con) + (mkInnerRhs con2tag_RDR op data_con) where as_needed = take (dataConSourceArity data_con) as_RDRs data_con_RDR = getRdrName data_con - mkInnerRhs dflags op data_con + mkInnerRhs con2tag_RDR op data_con | single_con_type = nlHsCase (nlHsVar b_RDR) [ mkInnerEqAlt op data_con ] @@ -435,14 +440,14 @@ gen_Ord_binds loc tycon = do , mkHsCaseAlt nlWildPat (gtResult op) ] | tag > last_tag `div` 2 -- lower range is larger - = untag_Expr dflags tycon [(b_RDR, bh_RDR)] $ + = untag_Expr con2tag_RDR [(b_RDR, bh_RDR)] $ nlHsIf (genPrimOpApp (nlHsVar bh_RDR) ltInt_RDR tag_lit) (gtResult op) $ -- Definitely GT nlHsCase (nlHsVar b_RDR) [ mkInnerEqAlt op data_con , mkHsCaseAlt nlWildPat (ltResult op) ] | otherwise -- upper range is larger - = untag_Expr dflags tycon [(b_RDR, bh_RDR)] $ + = untag_Expr con2tag_RDR [(b_RDR, bh_RDR)] $ nlHsIf (genPrimOpApp (nlHsVar bh_RDR) gtInt_RDR tag_lit) (ltResult op) $ -- Definitely LT nlHsCase (nlHsVar b_RDR) [ mkInnerEqAlt op data_con @@ -461,11 +466,11 @@ gen_Ord_binds loc tycon = do data_con_RDR = getRdrName data_con bs_needed = take (dataConSourceArity data_con) bs_RDRs - mkTagCmp :: DynFlags -> OrdOp -> LHsExpr GhcPs + mkTagCmp :: RdrName -> OrdOp -> LHsExpr GhcPs -- Both constructors known to be nullary -- generates (case data2Tag a of a# -> case data2Tag b of b# -> a# `op` b# - mkTagCmp dflags op = - untag_Expr dflags tycon[(a_RDR, ah_RDR),(b_RDR, bh_RDR)] $ + mkTagCmp con2tag_RDR op = + untag_Expr con2tag_RDR [(a_RDR, ah_RDR),(b_RDR, bh_RDR)] $ unliftedOrdOp intPrimTy op ah_RDR bh_RDR mkCompareFields :: OrdOp -> [Type] -> LHsExpr GhcPs @@ -585,78 +590,89 @@ For @enumFromTo@ and @enumFromThenTo@, we use the default methods. gen_Enum_binds :: SrcSpan -> TyCon -> TcM (LHsBinds GhcPs, BagDerivStuff) gen_Enum_binds loc tycon = do - dflags <- getDynFlags - return (method_binds dflags, aux_binds) + -- See Note [Auxiliary binders] + con2tag_occ <- chooseUniqueOccTc (mkCon2TagOcc (getOccName tycon)) + tag2con_occ <- chooseUniqueOccTc (mkTag2ConOcc (getOccName tycon)) + maxtag_occ <- chooseUniqueOccTc (mkMaxTagOcc (getOccName tycon)) + let con2tag_RDR = mkRdrUnqual con2tag_occ + tag2con_RDR = mkRdrUnqual tag2con_occ + maxtag_RDR = mkRdrUnqual maxtag_occ + + return ( method_binds con2tag_RDR tag2con_RDR maxtag_RDR + , aux_binds con2tag_RDR tag2con_RDR maxtag_RDR ) where - method_binds dflags = listToBag - [ succ_enum dflags - , pred_enum dflags - , to_enum dflags - , enum_from dflags -- [0 ..] - , enum_from_then dflags -- [0, 1 ..] - , from_enum dflags + method_binds con2tag_RDR tag2con_RDR maxtag_RDR = listToBag + [ succ_enum con2tag_RDR tag2con_RDR maxtag_RDR + , pred_enum con2tag_RDR tag2con_RDR + , to_enum tag2con_RDR maxtag_RDR + , enum_from con2tag_RDR tag2con_RDR maxtag_RDR -- [0 ..] + , enum_from_then con2tag_RDR tag2con_RDR maxtag_RDR -- [0, 1 ..] + , from_enum con2tag_RDR + ] + aux_binds con2tag_RDR tag2con_RDR maxtag_RDR = listToBag + [ genCon2Tag con2tag_RDR loc tycon + , genTag2Con tag2con_RDR loc tycon + , genMaxTag maxtag_RDR loc tycon ] - aux_binds = listToBag $ map DerivAuxBind - [DerivCon2Tag tycon, DerivTag2Con tycon, DerivMaxTag tycon] occ_nm = getOccString tycon - succ_enum dflags + succ_enum con2tag_RDR tag2con_RDR maxtag_RDR = mkSimpleGeneratedFunBind loc succ_RDR [a_Pat] $ - untag_Expr dflags tycon [(a_RDR, ah_RDR)] $ - nlHsIf (nlHsApps eq_RDR [nlHsVar (maxtag_RDR dflags tycon), + untag_Expr con2tag_RDR [(a_RDR, ah_RDR)] $ + nlHsIf (nlHsApps eq_RDR [nlHsVar maxtag_RDR, nlHsVarApps intDataCon_RDR [ah_RDR]]) (illegal_Expr "succ" occ_nm "tried to take `succ' of last tag in enumeration") - (nlHsApp (nlHsVar (tag2con_RDR dflags tycon)) + (nlHsApp (nlHsVar tag2con_RDR) (nlHsApps plus_RDR [nlHsVarApps intDataCon_RDR [ah_RDR], nlHsIntLit 1])) - pred_enum dflags + pred_enum con2tag_RDR tag2con_RDR = mkSimpleGeneratedFunBind loc pred_RDR [a_Pat] $ - untag_Expr dflags tycon [(a_RDR, ah_RDR)] $ + untag_Expr con2tag_RDR [(a_RDR, ah_RDR)] $ nlHsIf (nlHsApps eq_RDR [nlHsIntLit 0, nlHsVarApps intDataCon_RDR [ah_RDR]]) (illegal_Expr "pred" occ_nm "tried to take `pred' of first tag in enumeration") - (nlHsApp (nlHsVar (tag2con_RDR dflags tycon)) + (nlHsApp (nlHsVar tag2con_RDR) (nlHsApps plus_RDR [ nlHsVarApps intDataCon_RDR [ah_RDR] , nlHsLit (HsInt noExtField (mkIntegralLit (-1 :: Int)))])) - to_enum dflags + to_enum tag2con_RDR maxtag_RDR = mkSimpleGeneratedFunBind loc toEnum_RDR [a_Pat] $ nlHsIf (nlHsApps and_RDR [nlHsApps ge_RDR [nlHsVar a_RDR, nlHsIntLit 0], nlHsApps le_RDR [ nlHsVar a_RDR - , nlHsVar (maxtag_RDR dflags tycon)]]) - (nlHsVarApps (tag2con_RDR dflags tycon) [a_RDR]) - (illegal_toEnum_tag occ_nm (maxtag_RDR dflags tycon)) + , nlHsVar maxtag_RDR]]) + (nlHsVarApps tag2con_RDR [a_RDR]) + (illegal_toEnum_tag occ_nm maxtag_RDR) - enum_from dflags + enum_from con2tag_RDR tag2con_RDR maxtag_RDR = mkSimpleGeneratedFunBind loc enumFrom_RDR [a_Pat] $ - untag_Expr dflags tycon [(a_RDR, ah_RDR)] $ + untag_Expr con2tag_RDR [(a_RDR, ah_RDR)] $ nlHsApps map_RDR - [nlHsVar (tag2con_RDR dflags tycon), + [nlHsVar tag2con_RDR, nlHsPar (enum_from_to_Expr (nlHsVarApps intDataCon_RDR [ah_RDR]) - (nlHsVar (maxtag_RDR dflags tycon)))] + (nlHsVar maxtag_RDR))] - enum_from_then dflags + enum_from_then con2tag_RDR tag2con_RDR maxtag_RDR = mkSimpleGeneratedFunBind loc enumFromThen_RDR [a_Pat, b_Pat] $ - untag_Expr dflags tycon [(a_RDR, ah_RDR), (b_RDR, bh_RDR)] $ - nlHsApp (nlHsVarApps map_RDR [tag2con_RDR dflags tycon]) $ + untag_Expr con2tag_RDR [(a_RDR, ah_RDR), (b_RDR, bh_RDR)] $ + nlHsApp (nlHsVarApps map_RDR [tag2con_RDR]) $ nlHsPar (enum_from_then_to_Expr (nlHsVarApps intDataCon_RDR [ah_RDR]) (nlHsVarApps intDataCon_RDR [bh_RDR]) (nlHsIf (nlHsApps gt_RDR [nlHsVarApps intDataCon_RDR [ah_RDR], nlHsVarApps intDataCon_RDR [bh_RDR]]) (nlHsIntLit 0) - (nlHsVar (maxtag_RDR dflags tycon)) + (nlHsVar maxtag_RDR) )) - from_enum dflags + from_enum con2tag_RDR = mkSimpleGeneratedFunBind loc fromEnum_RDR [a_Pat] $ - untag_Expr dflags tycon [(a_RDR, ah_RDR)] $ + untag_Expr con2tag_RDR [(a_RDR, ah_RDR)] $ (nlHsVarApps intDataCon_RDR [ah_RDR]) {- @@ -757,35 +773,42 @@ we follow the scheme given in Figure~19 of the Haskell~1.2 report gen_Ix_binds :: SrcSpan -> TyCon -> TcM (LHsBinds GhcPs, BagDerivStuff) gen_Ix_binds loc tycon = do - dflags <- getDynFlags + -- See Note [Auxiliary binders] + con2tag_occ <- chooseUniqueOccTc (mkCon2TagOcc (getOccName tycon)) + tag2con_occ <- chooseUniqueOccTc (mkTag2ConOcc (getOccName tycon)) + let con2tag_RDR = mkRdrUnqual con2tag_occ + tag2con_RDR = mkRdrUnqual tag2con_occ + return $ if isEnumerationTyCon tycon - then (enum_ixes dflags, listToBag $ map DerivAuxBind - [DerivCon2Tag tycon, DerivTag2Con tycon, DerivMaxTag tycon]) - else (single_con_ixes, unitBag (DerivAuxBind (DerivCon2Tag tycon))) + then ( enum_ixes con2tag_RDR tag2con_RDR + , listToBag [ genCon2Tag con2tag_RDR loc tycon + , genTag2Con tag2con_RDR loc tycon + ]) + else (single_con_ixes, unitBag (genCon2Tag con2tag_RDR loc tycon)) where -------------------------------------------------------------- - enum_ixes dflags = listToBag - [ enum_range dflags - , enum_index dflags - , enum_inRange dflags + enum_ixes con2tag_RDR tag2con_RDR = listToBag + [ enum_range con2tag_RDR tag2con_RDR + , enum_index con2tag_RDR + , enum_inRange con2tag_RDR ] - enum_range dflags + enum_range con2tag_RDR tag2con_RDR = mkSimpleGeneratedFunBind loc range_RDR [nlTuplePat [a_Pat, b_Pat] Boxed] $ - untag_Expr dflags tycon [(a_RDR, ah_RDR)] $ - untag_Expr dflags tycon [(b_RDR, bh_RDR)] $ - nlHsApp (nlHsVarApps map_RDR [tag2con_RDR dflags tycon]) $ + untag_Expr con2tag_RDR [(a_RDR, ah_RDR)] $ + untag_Expr con2tag_RDR [(b_RDR, bh_RDR)] $ + nlHsApp (nlHsVarApps map_RDR [tag2con_RDR]) $ nlHsPar (enum_from_to_Expr (nlHsVarApps intDataCon_RDR [ah_RDR]) (nlHsVarApps intDataCon_RDR [bh_RDR])) - enum_index dflags + enum_index con2tag_RDR = mkSimpleGeneratedFunBind loc unsafeIndex_RDR [noLoc (AsPat noExtField (noLoc c_RDR) (nlTuplePat [a_Pat, nlWildPat] Boxed)), d_Pat] ( - untag_Expr dflags tycon [(a_RDR, ah_RDR)] ( - untag_Expr dflags tycon [(d_RDR, dh_RDR)] ( + untag_Expr con2tag_RDR [(a_RDR, ah_RDR)] ( + untag_Expr con2tag_RDR [(d_RDR, dh_RDR)] ( let rhs = nlHsVarApps intDataCon_RDR [c_RDR] in @@ -796,11 +819,11 @@ gen_Ix_binds loc tycon = do ) -- This produces something like `(ch >= ah) && (ch <= bh)` - enum_inRange dflags + enum_inRange con2tag_RDR = mkSimpleGeneratedFunBind loc inRange_RDR [nlTuplePat [a_Pat, b_Pat] Boxed, c_Pat] $ - untag_Expr dflags tycon [(a_RDR, ah_RDR)] ( - untag_Expr dflags tycon [(b_RDR, bh_RDR)] ( - untag_Expr dflags tycon [(c_RDR, ch_RDR)] ( + untag_Expr con2tag_RDR [(a_RDR, ah_RDR)] ( + untag_Expr con2tag_RDR [(b_RDR, bh_RDR)] ( + untag_Expr con2tag_RDR [(c_RDR, ch_RDR)] ( -- This used to use `if`, which interacts badly with RebindableSyntax. -- See #11396. nlHsApps and_RDR @@ -1324,54 +1347,17 @@ gen_Data_binds loc rep_tc dc_rdrs = map mkRdrUnqual dc_occs -- OK, now do the work - ; return (gen_data dflags dt_rdr dc_rdrs loc rep_tc) } - -gen_data :: DynFlags -> RdrName -> [RdrName] - -> SrcSpan -> TyCon - -> (LHsBinds GhcPs, -- The method bindings - BagDerivStuff) -- Auxiliary bindings -gen_data dflags data_type_name constr_names loc rep_tc - = (listToBag [gfoldl_bind, gunfold_bind, toCon_bind, dataTypeOf_bind] - `unionBags` gcast_binds, + ; pure ( listToBag [ gfoldl_bind, gunfold_bind + , toCon_bind dc_rdrs, dataTypeOf_bind dt_rdr ] + `unionBags` gcast_binds -- Auxiliary definitions: the data type and constructors - listToBag ( genDataTyCon - : zipWith genDataDataCon data_cons constr_names ) ) + , listToBag ( genDataTyCon dflags dt_rdr dc_rdrs loc rep_tc + : zipWith (genDataDataCon dt_rdr loc) data_cons dc_rdrs ) + ) } where data_cons = tyConDataCons rep_tc n_cons = length data_cons one_constr = n_cons == 1 - genDataTyCon :: DerivStuff - genDataTyCon -- $dT - = DerivHsBind (mkHsVarBind loc data_type_name rhs, - L loc (TypeSig noExtField [L loc data_type_name] sig_ty)) - - sig_ty = mkLHsSigWcType (nlHsTyVar dataType_RDR) - ctx = initDefaultSDocContext dflags - rhs = nlHsVar mkDataType_RDR - `nlHsApp` nlHsLit (mkHsString (showSDocOneLine ctx (ppr rep_tc))) - `nlHsApp` nlList (map nlHsVar constr_names) - - genDataDataCon :: DataCon -> RdrName -> DerivStuff - genDataDataCon dc constr_name -- $cT1 etc - = DerivHsBind (mkHsVarBind loc constr_name rhs, - L loc (TypeSig noExtField [L loc constr_name] sig_ty)) - where - sig_ty = mkLHsSigWcType (nlHsTyVar constr_RDR) - rhs = nlHsApps mkConstr_RDR constr_args - - constr_args - = [ -- nlHsIntLit (toInteger (dataConTag dc)), -- Tag - nlHsVar (data_type_name) -- DataType - , nlHsLit (mkHsString (occNameString dc_occ)) -- String name - , nlList labels -- Field labels - , nlHsVar fixity ] -- Fixity - - labels = map (nlHsLit . mkHsString . unpackFS . flLabel) - (dataConFieldLabels dc) - dc_occ = getOccName dc - is_infix = isDataSymOcc dc_occ - fixity | is_infix = infix_RDR - | otherwise = prefix_RDR ------------ gfoldl gfoldl_bind = mkFunBindEC 3 loc gfoldl_RDR id (map gfoldl_eqn data_cons) @@ -1410,16 +1396,18 @@ gen_data dflags data_type_name constr_names loc rep_tc tag = dataConTag dc ------------ toConstr - toCon_bind = mkFunBindEC 1 loc toConstr_RDR id - (zipWith to_con_eqn data_cons constr_names) + toCon_bind constr_names + = mkFunBindEC 1 loc toConstr_RDR id + (zipWith to_con_eqn data_cons constr_names) to_con_eqn dc con_name = ([nlWildConPat dc], nlHsVar con_name) ------------ dataTypeOf - dataTypeOf_bind = mkSimpleGeneratedFunBind - loc - dataTypeOf_RDR - [nlWildPat] - (nlHsVar data_type_name) + dataTypeOf_bind data_type_name + = mkSimpleGeneratedFunBind + loc + dataTypeOf_RDR + [nlWildPat] + (nlHsVar data_type_name) ------------ gcast1/2 -- Make the binding dataCast1 x = gcast1 x -- if T :: * -> * @@ -1934,7 +1922,7 @@ mkCoerceClassMethEqn cls inst_tvs inst_tys rhs_ty id {- ************************************************************************ * * -\subsection{Generating extra binds (@con2tag@ and @tag2con@)} +\subsection{Generating extra binds (@con2tag@, @tag2con@, etc.)} * * ************************************************************************ @@ -1950,75 +1938,18 @@ The `tags' here start at zero, hence the @fIRST_TAG@ (currently one) fiddling around. -} -genAuxBindSpec :: DynFlags -> SrcSpan -> AuxBindSpec - -> (LHsBind GhcPs, LSig GhcPs) -genAuxBindSpec dflags loc (DerivCon2Tag tycon) - = (mkFunBindSE 0 loc rdr_name eqns, - L loc (TypeSig noExtField [L loc rdr_name] sig_ty)) - where - rdr_name = con2tag_RDR dflags tycon - - sig_ty = mkLHsSigWcType $ L loc $ XHsType $ NHsCoreTy $ - mkSpecSigmaTy (tyConTyVars tycon) (tyConStupidTheta tycon) $ - mkParentType tycon `mkVisFunTy` intPrimTy - - lots_of_constructors = tyConFamilySize tycon > 8 - -- was: mAX_FAMILY_SIZE_FOR_VEC_RETURNS - -- but we don't do vectored returns any more. - - eqns | lots_of_constructors = [get_tag_eqn] - | otherwise = map mk_eqn (tyConDataCons tycon) - - get_tag_eqn = ([nlVarPat a_RDR], nlHsApp (nlHsVar getTag_RDR) a_Expr) - - mk_eqn :: DataCon -> ([LPat GhcPs], LHsExpr GhcPs) - mk_eqn con = ([nlWildConPat con], - nlHsLit (HsIntPrim NoSourceText - (toInteger ((dataConTag con) - fIRST_TAG)))) - -genAuxBindSpec dflags loc (DerivTag2Con tycon) - = (mkFunBindSE 0 loc rdr_name - [([nlConVarPat intDataCon_RDR [a_RDR]], - nlHsApp (nlHsVar tagToEnum_RDR) a_Expr)], - L loc (TypeSig noExtField [L loc rdr_name] sig_ty)) - where - sig_ty = mkLHsSigWcType $ L loc $ - XHsType $ NHsCoreTy $ mkSpecForAllTys (tyConTyVars tycon) $ - intTy `mkVisFunTy` mkParentType tycon - - rdr_name = tag2con_RDR dflags tycon - -genAuxBindSpec dflags loc (DerivMaxTag tycon) - = (mkHsVarBind loc rdr_name rhs, - L loc (TypeSig noExtField [L loc rdr_name] sig_ty)) - where - rdr_name = maxtag_RDR dflags tycon - sig_ty = mkLHsSigWcType (L loc (XHsType (NHsCoreTy intTy))) - rhs = nlHsApp (nlHsVar intDataCon_RDR) - (nlHsLit (HsIntPrim NoSourceText max_tag)) - max_tag = case (tyConDataCons tycon) of - data_cons -> toInteger ((length data_cons) - fIRST_TAG) - type SeparateBagsDerivStuff = -- AuxBinds and SYB bindings ( Bag (LHsBind GhcPs, LSig GhcPs) -- Extra family instances (used by Generic and DeriveAnyClass) , Bag (FamInst) ) -genAuxBinds :: DynFlags -> SrcSpan -> BagDerivStuff -> SeparateBagsDerivStuff -genAuxBinds dflags loc b = genAuxBinds' b2 where - (b1,b2) = partitionBagWith splitDerivAuxBind b - splitDerivAuxBind (DerivAuxBind x) = Left x - splitDerivAuxBind x = Right x - - rm_dups = foldr dup_check emptyBag - dup_check a b = if anyBag (== a) b then b else consBag a b - +genAuxBinds :: BagDerivStuff -> SeparateBagsDerivStuff +genAuxBinds b = genAuxBinds' b where genAuxBinds' :: BagDerivStuff -> SeparateBagsDerivStuff - genAuxBinds' = foldr f ( mapBag (genAuxBindSpec dflags loc) (rm_dups b1) - , emptyBag ) + genAuxBinds' = foldr f (emptyBag, emptyBag) + f :: DerivStuff -> SeparateBagsDerivStuff -> SeparateBagsDerivStuff - f (DerivAuxBind _) = panic "genAuxBinds'" -- We have removed these before f (DerivHsBind b) = add1 b f (DerivFamInst t) = add2 t @@ -2258,13 +2189,12 @@ eq_Expr ty a b where (_, _, prim_eq, _, _) = primOrdOps "Eq" ty -untag_Expr :: DynFlags -> TyCon -> [( RdrName, RdrName)] +untag_Expr :: RdrName -> [( RdrName, RdrName)] -> LHsExpr GhcPs -> LHsExpr GhcPs -untag_Expr _ _ [] expr = expr -untag_Expr dflags tycon ((untag_this, put_tag_here) : more) expr - = nlHsCase (nlHsPar (nlHsVarApps (con2tag_RDR dflags tycon) - [untag_this])) {-of-} - [mkHsCaseAlt (nlVarPat put_tag_here) (untag_Expr dflags tycon more expr)] +untag_Expr _ [] expr = expr +untag_Expr con2tag_RDR ((untag_this, put_tag_here) : more) expr + = nlHsCase (nlHsPar (nlHsVarApps con2tag_RDR [untag_this])) {-of-} + [mkHsCaseAlt (nlVarPat put_tag_here) (untag_Expr con2tag_RDR more expr)] enum_from_to_Expr :: LHsExpr GhcPs -> LHsExpr GhcPs @@ -2376,54 +2306,203 @@ minusInt_RDR, tagToEnum_RDR :: RdrName minusInt_RDR = getRdrName (primOpId IntSubOp ) tagToEnum_RDR = getRdrName (primOpId TagToEnumOp) -con2tag_RDR, tag2con_RDR, maxtag_RDR :: DynFlags -> TyCon -> RdrName --- Generates Orig s RdrName, for the binding positions -con2tag_RDR dflags tycon = mk_tc_deriv_name dflags tycon mkCon2TagOcc -tag2con_RDR dflags tycon = mk_tc_deriv_name dflags tycon mkTag2ConOcc -maxtag_RDR dflags tycon = mk_tc_deriv_name dflags tycon mkMaxTagOcc - -mk_tc_deriv_name :: DynFlags -> TyCon -> (OccName -> OccName) -> RdrName -mk_tc_deriv_name dflags tycon occ_fun = - mkAuxBinderName dflags (tyConName tycon) occ_fun - -mkAuxBinderName :: DynFlags -> Name -> (OccName -> OccName) -> RdrName --- ^ Make a top-level binder name for an auxiliary binding for a parent name --- See Note [Auxiliary binders] -mkAuxBinderName dflags parent occ_fun - = mkRdrUnqual (occ_fun stable_parent_occ) +-- Generate @$con2tag_T :: T -> Int#@, which returns the tag corresponding to a +-- constructor of type @T@. +genCon2Tag :: RdrName -> SrcSpan -> TyCon -> DerivStuff +genCon2Tag con2tag_RDR loc tycon + = DerivHsBind (mkFunBindSE 0 loc con2tag_RDR eqns, + L loc (TypeSig noExtField [L loc con2tag_RDR] sig_ty)) + where + sig_ty = mkLHsSigWcType $ L loc $ XHsType $ NHsCoreTy $ + mkSpecSigmaTy (tyConTyVars tycon) (tyConStupidTheta tycon) $ + mkParentType tycon `mkVisFunTy` intPrimTy + + lots_of_constructors = tyConFamilySize tycon > 8 + -- was: mAX_FAMILY_SIZE_FOR_VEC_RETURNS + -- but we don't do vectored returns any more. + + eqns | lots_of_constructors = [get_tag_eqn] + | otherwise = map mk_eqn (tyConDataCons tycon) + + get_tag_eqn = ([nlVarPat a_RDR], nlHsApp (nlHsVar getTag_RDR) a_Expr) + + mk_eqn :: DataCon -> ([LPat GhcPs], LHsExpr GhcPs) + mk_eqn con = ([nlWildConPat con], + nlHsLit (HsIntPrim NoSourceText + (toInteger ((dataConTag con) - fIRST_TAG)))) + +-- Generate @$tag2con_T :: Int -> T@, which returns the data constructor (of +-- type @T@) corresponding to a particular tag. +genTag2Con :: RdrName -> SrcSpan -> TyCon -> DerivStuff +genTag2Con tag2con_RDR loc tycon + = DerivHsBind (mkFunBindSE 0 loc tag2con_RDR + [([nlConVarPat intDataCon_RDR [a_RDR]], + nlHsApp (nlHsVar tagToEnum_RDR) a_Expr)], + L loc (TypeSig noExtField [L loc tag2con_RDR] sig_ty)) + where + sig_ty = mkLHsSigWcType $ L loc $ + XHsType $ NHsCoreTy $ mkSpecForAllTys (tyConTyVars tycon) $ + intTy `mkVisFunTy` mkParentType tycon + +-- Generate @$maxtag_T :: Int@, which returns the maximum tag for the data +-- constructors of some data type @T@. +genMaxTag :: RdrName -> SrcSpan -> TyCon -> DerivStuff +genMaxTag maxtag_RDR loc tycon + = DerivHsBind (mkHsVarBind loc maxtag_RDR rhs, + L loc (TypeSig noExtField [L loc maxtag_RDR] sig_ty)) + where + sig_ty = mkLHsSigWcType (L loc (XHsType (NHsCoreTy intTy))) + rhs = nlHsApp (nlHsVar intDataCon_RDR) + (nlHsLit (HsIntPrim NoSourceText max_tag)) + max_tag = case (tyConDataCons tycon) of + data_cons -> toInteger ((length data_cons) - fIRST_TAG) + +-- Generate @$tT :: Data.Data.DataType@, which contains the representation for +-- the data type @T@. +genDataTyCon :: DynFlags -> RdrName -> [RdrName] -> SrcSpan -> TyCon -> DerivStuff +genDataTyCon dflags data_type_name constr_names loc rep_tc + = DerivHsBind (mkHsVarBind loc data_type_name rhs, + L loc (TypeSig noExtField [L loc data_type_name] sig_ty)) where - stable_parent_occ = mkOccName (occNameSpace parent_occ) stable_string - stable_string - | hasPprDebug dflags = parent_stable - | otherwise = parent_stable_hash - parent_stable = nameStableString parent - parent_stable_hash = - let Fingerprint high low = fingerprintString parent_stable - in toBase62 high ++ toBase62Padded low - -- See Note [Base 62 encoding 128-bit integers] in GHC.Utils.Encoding - parent_occ = nameOccName parent + sig_ty = mkLHsSigWcType (nlHsTyVar dataType_RDR) + ctx = initDefaultSDocContext dflags + rhs = nlHsVar mkDataType_RDR + `nlHsApp` nlHsLit (mkHsString (showSDocOneLine ctx (ppr rep_tc))) + `nlHsApp` nlList (map nlHsVar constr_names) +-- Generate @$cD :: Data.Data.Constr@, which contains the representation for +-- the data constructor @D@. +genDataDataCon :: RdrName -> SrcSpan -> DataCon -> RdrName -> DerivStuff +genDataDataCon data_type_name loc dc constr_name + = DerivHsBind (mkHsVarBind loc constr_name rhs, + L loc (TypeSig noExtField [L loc constr_name] sig_ty)) + where + sig_ty = mkLHsSigWcType (nlHsTyVar constr_RDR) + rhs = nlHsApps mkConstr_RDR constr_args + + constr_args + = [ -- nlHsIntLit (toInteger (dataConTag dc)), -- Tag + nlHsVar (data_type_name) -- DataType + , nlHsLit (mkHsString (occNameString dc_occ)) -- String name + , nlList labels -- Field labels + , nlHsVar fixity ] -- Fixity + + labels = map (nlHsLit . mkHsString . unpackFS . flLabel) + (dataConFieldLabels dc) + dc_occ = getOccName dc + is_infix = isDataSymOcc dc_occ + fixity | is_infix = infix_RDR + | otherwise = prefix_RDR {- Note [Auxiliary binders] ~~~~~~~~~~~~~~~~~~~~~~~~ -We often want to make a top-level auxiliary binding. E.g. for comparison we have +We often want to make a top-level auxiliary bindings in derived instances. +E.g. for derived Ord instances we have: + + data T = ... + deriving instance Ord T + + ==> instance Ord T where - compare a b = $con2tag a `compare` $con2tag b + compare a b = $con2tag_T a `compare` $con2tag_T b + + $con2tag_T :: T -> Int + $con2tag_T = ...code.... + +Note that multiple instances of the same type might need to use the same sort +of auxiliary binding. For example, $con2tag is used not only in derived Ord +instances, but also in derived Eq instances: + + deriving instance Eq T + + ==> + + instance Eq T where + a == b = $con2tag_T a == $con2tag_T b + +In fact, there are situations where the same sort of auxiliary binding can be +used in multiple instances of the same type /and/ the same type class. Consider +this example: + + data S a = ... + + deriving instance Eq (S Int) + deriving instance Eq (S Bool) + + ==> + + instance Eq (S Int) where + a == b = $con2tag_S a == $con2tag_S b + instance Eq (S Int) where + a == b = $con2tag_S a == $con2tag_S b + + $con2tag_S :: S a -> Int + $con2tag_S = ...code.... + +These examples can prove challenging because if GHC implements `deriving` in a +naïve way, then it is easy to generate multiple copies of the same sort of +auxiliary binding per type constructor /with the same name/. When these copies +are colocated in the same module, bad things usually follow. +See #7947, #12245, and #18321 for examples of bad things that can happen. + +Note that we are really only concerned about duplicate auxiliary binding names +within the same module. If there are two copies of $con2tag_T in two different +modules, then we don't have to worry, since GHC will be able to distinguish +them. In order to avoid duplicates within the same module, we have considered +three options: + +1. Always generate a unique copy of each sort of auxiliary binding per data type + definition, and use these unique copies across all derived instances for + each data type. + +2. In each derived instance, generate a copy of each sort of auxiliary + binding, but without any effort to make the names unique. Later, perform a + deduplication pass to ensure that there is only one copy per module. + +3. In each derived instance, generate a copy of each sort of auxiliary + binding, giving each copy a unique name. + +Here are the pros and cons of each approach: + +1. Pros: Because each data type has unique auxiliary bindings associated with + it, there is no danger of having name clashes between instances that use + these auxiliary bindings. + + Cons: GHC would need to make sure that the implementations for each sort of + auxiliary binding are available for each data type. If a data type never + has any derived instances that use these bindings, then users pay the cost + of compiling extra code that never gets used. + +2. Pros: If implementing correctly, this option ensures that at most one copy + of each sort of auxiliary binding is defined per data type. Therefore, this + option promises to have the least code bloat. + + Cons: It's surprisingly awkward to implement this in practice, especially + when instances across Template Haskell splice boundaries (see #18321). It's + possible to deduplicate auxiliary bindings with a dedicated pass in the + renamer, but it's rather grungy. + (See https://gitlab.haskell.org/ghc/ghc/issues/18321#note_281178.) - $con2tag :: T -> Int - $con2tag = ...code.... +3. Pros: This option is conceptually very simple to implement, as it requires + no manual deduplication. Just generate auxiliary bindings for each instance + on the fly, and use a unique number as a suffix to avoid intra-module name + clashes. -Of course these top-level bindings should all have distinct name, and we are -generating RdrNames here. We can't just use the TyCon or DataCon to distinguish -because with standalone deriving two imported TyCons might both be called T! -(See #7947.) + Cons: It's easy to end up with multiple copies of the same sort of auxiliary + binding, but with different names. This increases the potential for code + bloat, but given that most auxiliary bindings are quite small in size, a + CSE pass will often deduplicate these copies anyway when optimizations are + enabled. -So we use package name, module name and the name of the parent -(T in this example) as part of the OccName we generate for the new binding. -To make the symbol names short we take a base62 hash of the full name. +In practice, GHC implements option (3). As a result, any gen_* function defined +in this module that needs to use auxiliary bindings must use TcM. This is +because each auxiliary binding name needs a unique number, so these unique +numbers are taken from a global counter that is incremented each time an +auxiliary binding is defined. -In the past we used the *unique* from the parent, but that's not stable across -recompilations as uniques are nondeterministic. +In the past we used the Unique from the parent TyCon rather than taking it from +a dedicated global counter, but that's not stable across recompilations as +Uniques are nondeterministic. -} diff --git a/compiler/GHC/Tc/Deriv/Utils.hs b/compiler/GHC/Tc/Deriv/Utils.hs index 66adb4e554..478ff0b6fe 100644 --- a/compiler/GHC/Tc/Deriv/Utils.hs +++ b/compiler/GHC/Tc/Deriv/Utils.hs @@ -590,6 +590,13 @@ hasStockDeriving clas = let (binds, deriv_stuff) = gen_fn loc tc in return (binds, deriv_stuff, []) + -- Like 'simple', but monadic. The only monadic things these functions do + -- at present are: + -- + -- - Obtain the DynFlags + -- + -- - Generate unique numbers for auxiliary binding names. + -- See Note [Auxiliary binders] in GHC.Tc.Deriv.Generate. simpleM gen_fn loc tc _ = do { (binds, deriv_stuff) <- gen_fn loc tc ; return (binds, deriv_stuff, []) } diff --git a/compiler/GHC/Types/Name/Occurrence.hs b/compiler/GHC/Types/Name/Occurrence.hs index 2062d5449b..7b11d670fb 100644 --- a/compiler/GHC/Types/Name/Occurrence.hs +++ b/compiler/GHC/Types/Name/Occurrence.hs @@ -608,7 +608,6 @@ mkDataConWrapperOcc, mkWorkerOcc, mkGenR, mkGen1R, mkDataConWorkerOcc, mkNewTyCoOcc, mkInstTyCoOcc, mkEqPredCoOcc, mkClassOpAuxOcc, - mkCon2TagOcc, mkTag2ConOcc, mkMaxTagOcc, mkTyConRepOcc :: OccName -> OccName @@ -629,11 +628,6 @@ mkNewTyCoOcc = mk_simple_deriv tcName "N:" -- Coercion for newtypes mkInstTyCoOcc = mk_simple_deriv tcName "D:" -- Coercion for type functions mkEqPredCoOcc = mk_simple_deriv tcName "$co" --- Used in derived instances -mkCon2TagOcc = mk_simple_deriv varName "$con2tag_" -mkTag2ConOcc = mk_simple_deriv varName "$tag2con_" -mkMaxTagOcc = mk_simple_deriv varName "$maxtag_" - -- TyConRepName stuff; see Note [Grand plan for Typeable] in GHC.Tc.Instance.Typeable mkTyConRepOcc occ = mk_simple_deriv varName prefix occ where @@ -697,15 +691,17 @@ mkDFunOcc info_str is_boot set prefix | is_boot = "$fx" | otherwise = "$f" -mkDataTOcc, mkDataCOcc +mkCon2TagOcc, mkTag2ConOcc, mkMaxTagOcc, mkDataTOcc, mkDataCOcc :: OccName -- ^ TyCon or data con string -> OccSet -- ^ avoid these Occs -> OccName -- ^ E.g. @$f3OrdMaybe@ --- data T = MkT ... deriving( Data ) needs definitions for --- $tT :: Data.Generics.Basics.DataType --- $cMkT :: Data.Generics.Basics.Constr -mkDataTOcc occ = chooseUniqueOcc VarName ("$t" ++ occNameString occ) -mkDataCOcc occ = chooseUniqueOcc VarName ("$c" ++ occNameString occ) +-- Generates the names of auxiliary bindings used for derived instances. +-- See Note [Auxiliary binders] in GHC.Tc.Deriv.Generate. +mkCon2TagOcc occ = chooseUniqueOcc VarName ("$con2tag_" ++ occNameString occ) +mkTag2ConOcc occ = chooseUniqueOcc VarName ("$tag2con_" ++ occNameString occ) +mkMaxTagOcc occ = chooseUniqueOcc VarName ("$maxtag_" ++ occNameString occ) +mkDataTOcc occ = chooseUniqueOcc VarName ("$t" ++ occNameString occ) +mkDataCOcc occ = chooseUniqueOcc VarName ("$c" ++ occNameString occ) {- Sometimes we need to pick an OccName that has not already been used, diff --git a/testsuite/tests/deriving/should_compile/T14682.stderr b/testsuite/tests/deriving/should_compile/T14682.stderr index 59fc405cdb..ca81b45b2d 100644 --- a/testsuite/tests/deriving/should_compile/T14682.stderr +++ b/testsuite/tests/deriving/should_compile/T14682.stderr @@ -71,14 +71,13 @@ Derived class instances: = (GHC.Ix.inRange (a1, b1) c1 GHC.Classes.&& GHC.Ix.inRange (a2, b2) c2) - T14682.$con2tag_B4iUvrAY4wB3YczpMJQUOX :: - T14682.Foo -> GHC.Prim.Int# - T14682.$con2tag_B4iUvrAY4wB3YczpMJQUOX (T14682.Foo _ _) = 0# T14682.$tFoo :: Data.Data.DataType T14682.$cFoo :: Data.Data.Constr T14682.$tFoo = Data.Data.mkDataType "Foo" [T14682.$cFoo] T14682.$cFoo = Data.Data.mkConstr T14682.$tFoo "Foo" [] Data.Data.Prefix + T14682.$con2tag_Foo1 :: T14682.Foo -> GHC.Prim.Int# + T14682.$con2tag_Foo1 (T14682.Foo _ _) = 0# Derived type family instances: diff --git a/testsuite/tests/deriving/should_compile/T18321.hs b/testsuite/tests/deriving/should_compile/T18321.hs new file mode 100644 index 0000000000..5391cf602b --- /dev/null +++ b/testsuite/tests/deriving/should_compile/T18321.hs @@ -0,0 +1,15 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TemplateHaskell #-} +module T18321 where + +import Data.Ix + +data T = MkT deriving (Eq, Ord, Ix) +$(return []) +deriving instance Enum T + +data S a = MkS +deriving instance Enum (S Int) +$(return []) +deriving instance Enum (S Bool) diff --git a/testsuite/tests/deriving/should_compile/all.T b/testsuite/tests/deriving/should_compile/all.T index 8a363e72f9..f6e9d43b06 100644 --- a/testsuite/tests/deriving/should_compile/all.T +++ b/testsuite/tests/deriving/should_compile/all.T @@ -124,3 +124,4 @@ test('T17339', normal, compile, ['-ddump-simpl -dsuppress-idinfo -dno-typeable-binds']) test('T17880', normal, compile, ['']) test('T18055', normal, compile, ['']) +test('T18321', normal, compile, ['']) |