Skip to content
This repository was archived by the owner on Aug 3, 2024. It is now read-only.

More synify-ing improvements #931

Merged
merged 13 commits into from
Sep 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions haddock-api/src/Haddock/Backends/LaTeX.hs
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,7 @@ tupleParens _ = parenList


sumParens :: [LaTeX] -> LaTeX
sumParens = ubxparens . hsep . punctuate (text " | ")
sumParens = ubxparens . hsep . punctuate (text " |")


-------------------------------------------------------------------------------
Expand Down Expand Up @@ -1335,7 +1335,7 @@ ubxParenList = ubxparens . hsep . punctuate comma


ubxparens :: LaTeX -> LaTeX
ubxparens h = text "(#" <> h <> text "#)"
ubxparens h = text "(#" <+> h <+> text "#)"


nl :: LaTeX
Expand Down
2 changes: 1 addition & 1 deletion haddock-api/src/Haddock/Backends/Xhtml/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ ubxSumList = ubxparens . hsep . punctuate (toHtml " | ")


ubxparens :: Html -> Html
ubxparens h = toHtml "(#" +++ h +++ toHtml "#)"
ubxparens h = toHtml "(#" <+> h <+> toHtml "#)"


dcolon, arrow, darrow, forallSymbol :: Bool -> Html
Expand Down
318 changes: 200 additions & 118 deletions haddock-api/src/Haddock/Convert.hs

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions haddock-api/src/Haddock/GhcUtils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Control.Arrow
import Haddock.Types( DocNameI )

import Exception
import FV
import Outputable
import Name
import NameSet
Expand All @@ -30,6 +31,9 @@ import HscTypes
import GHC
import Class
import DynFlags
import Var ( TyVarBndr(..), TyVarBinder, tyVarKind )
import VarSet ( VarSet, emptyVarSet )
import TyCoRep ( Type(..) )

import HsTypes (HsType(..))

Expand Down Expand Up @@ -436,3 +440,62 @@ setStubDir f d = d{ stubDir = Just f
setOutputDir f = setObjectDir f . setHiDir f . setStubDir f


-------------------------------------------------------------------------------
-- * Free variables of a 'Type'
-------------------------------------------------------------------------------

-- | Get free type variables in a 'Type' in their order of appearance.
-- See [Ordering of implicit variables].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must be missing some context here. Why would we ever want to retrieve free type variables in reverse order?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want them in order. Unfortunately, that's not what GHC does.

unionFV starts by running the second FV on the accumulator, then passing that to the first FV. That means it adds variables from the second argument to the in_scope set before adding variables from the first argument. All this is bad news if the first and second arguments share some variables.

Here is a snippet demonstrating the somewhat surprising behaviour:

ghci -package ghc
ghci> import FV
ghci> import TysWiredIn
ghci> import Unique
ghci> import Name
ghci> import OccName
ghci> import Id
ghci> x0 = mkLocalId (mkSystemName (mkUniqueGrimily 0) (mkVarOcc "x0")) unitTy
ghci> x1 = mkLocalId (mkSystemName (mkUniqueGrimily 1) (mkVarOcc "x1")) unitTy
ghci> tvs = fvVarList (unitFV x0 `unionFV` unitFV x1 `unionFV` unitFV x0)
ghci> map (getOccString . idName) tvs
["x1","x0"]

My hack here is to traverse everything in reverse order, then reverse the list of type variables at the end.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh dear, that's absolutely terrible. I really thought that tyCoVarsOfTypesWellScoped would be enough to accomplish this task, but your counterexample shows that I was mistaken.

Still, I'm incredibly bothered by the fact that we have to duplicate so much machinery just to solve this problem. I've sent an email to the ghc-devs list asking about this, so I'm hoping I can get some clarification on this point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on Simon's response here, it looks like the FV-returning functions really don't guarantee any particular order (only that it's deterministic), which is a huge bummer.

Ideally, we'd rewrite tyCoFVsOfType and friends in such a way that they always return things in left-to-right order. Your hacked version accomplishes this, but at the cost of reversing the order at the very end, which feels wasteful. Is there a way to accomplish the same thing with only one pass?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to accomplish the same thing with only one pass?

In GHC: yes. We'd need to change unitFV to append to the var list. In order to keep that operation efficient, that means swapping occurrences of [Var] in FV for difference lists of Var. It looks like the FV code has already been carefully performance tuned, so we'd have to be very careful there.

In Haddock: not really, short of porting over most of FV (and then making the changes we'd otherwise make in GHC).

What frustrates me the most is not the reverse: it's having to rely on what is accidental behaviour in GHC.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, this is all so nasty.

Ultimately, I think the orderedFVs hack is probably the way to go for now. I'd only request that you expand the documentation for it to include an example of why tyCoVarsOfTypeWellScoped doesn't give the behavior you desire (e.g., your example in #931 (comment)).

Ideally, we'd investigate making the FV functions in GHC return things in a reliable left-to-right order. But as you've noted, they're quite performance intensive, so I can understand your reservations about making things slower by changing the underlying data structures involved.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added an example illustrating GHC's behaviour (I didn't know about alphaTyVar and betaTyVar - thanks!).

I agree we should eventually look into making FV return things in a reliable left-to-right order. I'm a bit swamped at the moment, but I'll try to circle back when all of this Hi Haddock stuff settles down.

orderedFVs
:: VarSet -- ^ free variables to ignore
-> [Type] -- ^ types to traverse (in order) looking for free variables
-> [TyVar] -- ^ free type variables, in the order they appear in
orderedFVs vs tys =
reverse . fst $ tyCoFVsOfTypes' tys (const True) vs ([], emptyVarSet)


-- See the "Free variables of types and coercions" section in 'TyCoRep', or
-- check out Note [Free variables of types]. The functions in this section
-- don't output type variables in the order they first appear in in the 'Type'.
--
-- For example, 'tyCoVarsOfTypeList' reports an incorrect order for the type
-- of 'const :: a -> b -> a':
--
-- >>> import Name
-- >>> import TyCoRep
-- >>> import TysPrim
-- >>> import Var
-- >>> a = TyVarTy alphaTyVar
-- >>> b = TyVarTy betaTyVar
-- >>> constTy = mkFunTys [a, b] a
-- >>> map (getOccString . tyVarName) (tyCoVarsOfTypeList constTy)
-- ["b","a"]
--
-- However, we want to reuse the very optimized traversal machinery there, so
-- so we make our own `tyCoFVsOfType'`, `tyCoFVsBndr'`, and `tyCoVarsOfTypes'`.
-- All these do differently is traverse in a different order and ignore
-- coercion variables.

-- | Just like 'tyCoFVsOfType', but traverses type variables in reverse order
-- of appearance.
tyCoFVsOfType' :: Type -> FV
tyCoFVsOfType' (TyVarTy v) a b c = (FV.unitFV v `unionFV` tyCoFVsOfType' (tyVarKind v)) a b c
tyCoFVsOfType' (TyConApp _ tys) a b c = tyCoFVsOfTypes' tys a b c
tyCoFVsOfType' (LitTy {}) a b c = emptyFV a b c
tyCoFVsOfType' (AppTy fun arg) a b c = (tyCoFVsOfType' arg `unionFV` tyCoFVsOfType' fun) a b c
tyCoFVsOfType' (FunTy arg res) a b c = (tyCoFVsOfType' res `unionFV` tyCoFVsOfType' arg) a b c
tyCoFVsOfType' (ForAllTy bndr ty) a b c = tyCoFVsBndr' bndr (tyCoFVsOfType' ty) a b c
tyCoFVsOfType' (CastTy ty _) a b c = (tyCoFVsOfType' ty) a b c
tyCoFVsOfType' (CoercionTy _ ) a b c = emptyFV a b c

-- | Just like 'tyCoFVsOfTypes', but traverses type variables in reverse order
-- of appearance.
tyCoFVsOfTypes' :: [Type] -> FV
tyCoFVsOfTypes' (ty:tys) fv_cand in_scope acc = (tyCoFVsOfTypes' tys `unionFV` tyCoFVsOfType' ty) fv_cand in_scope acc
tyCoFVsOfTypes' [] fv_cand in_scope acc = emptyFV fv_cand in_scope acc

-- | Just like 'tyCoFVsBndr', but traverses type variables in reverse order of
-- appearance.
tyCoFVsBndr' :: TyVarBinder -> FV -> FV
tyCoFVsBndr' (TvBndr tv _) fvs = FV.delFV tv fvs `unionFV` tyCoFVsOfType' (tyVarKind tv)

2 changes: 1 addition & 1 deletion hoogle-test/ref/Bug722/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Bug722
class Foo a
(!@#) :: Foo a => a -> a -> a
infixl 4 !@#
type family &* :: * -> * -> *
type family (&*) :: * -> * -> *
infixr 3 &*
data a :-& b
(:^&) :: a -> b -> (:-&) a b
Expand Down
196 changes: 196 additions & 0 deletions html-test/ref/UnboxedStuff.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<html xmlns="http://www.w3.org/1999/xhtml"
><head
><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"
/><title
>UnboxedStuff</title
><link href="#" rel="stylesheet" type="text/css" title="Ocean"
/><link rel="stylesheet" type="text/css" href="#"
/><script src="haddock-bundle.min.js" async="async" type="text/javascript"
></script
><script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"
></script
></head
><body
><div id="package-header"
><ul class="links" id="page-menu"
><li
><a href="#"
>Contents</a
></li
><li
><a href="#"
>Index</a
></li
></ul
><p class="caption empty"
></p
></div
><div id="content"
><div id="module-header"
><table class="info"
><tr
><th
>Safe Haskell</th
><td
>Safe</td
></tr
></table
><p class="caption"
>UnboxedStuff</p
></div
><div id="table-of-contents"
><p class="caption"
>Contents</p
><ul
><li
><a href="#"
>Unboxed type constructors</a
></li
></ul
></div
><div id="synopsis"
><details id="syn"
><summary
>Synopsis</summary
><ul class="details-toggle" data-details-id="syn"
><li class="src short"
><span class="keyword"
>data</span
> <a href="#"
>X</a
></li
><li class="src short"
><span class="keyword"
>data</span
> <a href="#"
>Y</a
></li
><li class="src short"
><span class="keyword"
>data</span
> <a href="#"
>Z</a
></li
><li class="src short"
><a href="#"
>unboxedUnit</a
> :: (# #) -&gt; (# #)</li
><li class="src short"
><a href="#"
>unboxedTuple</a
> :: (# <a href="#" title="UnboxedStuff"
>X</a
>, <a href="#" title="UnboxedStuff"
>Y</a
> #) -&gt; (# <a href="#" title="UnboxedStuff"
>X</a
>, <a href="#" title="UnboxedStuff"
>Y</a
>, <a href="#" title="UnboxedStuff"
>Z</a
> #)</li
><li class="src short"
><a href="#"
>unboxedSum</a
> :: (# <a href="#" title="UnboxedStuff"
>X</a
> | <a href="#" title="UnboxedStuff"
>Y</a
> #) -&gt; (# <a href="#" title="UnboxedStuff"
>X</a
> | <a href="#" title="UnboxedStuff"
>Y</a
> | <a href="#" title="UnboxedStuff"
>Z</a
> #)</li
></ul
></details
></div
><div id="interface"
><h1
>Documentation</h1
><div class="top"
><p class="src"
><span class="keyword"
>data</span
> <a id="t:X" class="def"
>X</a
> <a href="#" class="selflink"
>#</a
></p
></div
><div class="top"
><p class="src"
><span class="keyword"
>data</span
> <a id="t:Y" class="def"
>Y</a
> <a href="#" class="selflink"
>#</a
></p
></div
><div class="top"
><p class="src"
><span class="keyword"
>data</span
> <a id="t:Z" class="def"
>Z</a
> <a href="#" class="selflink"
>#</a
></p
></div
><a href="#" id="g:1"
><h1
>Unboxed type constructors</h1
></a
><div class="top"
><p class="src"
><a id="v:unboxedUnit" class="def"
>unboxedUnit</a
> :: (# #) -&gt; (# #) <a href="#" class="selflink"
>#</a
></p
></div
><div class="top"
><p class="src"
><a id="v:unboxedTuple" class="def"
>unboxedTuple</a
> :: (# <a href="#" title="UnboxedStuff"
>X</a
>, <a href="#" title="UnboxedStuff"
>Y</a
> #) -&gt; (# <a href="#" title="UnboxedStuff"
>X</a
>, <a href="#" title="UnboxedStuff"
>Y</a
>, <a href="#" title="UnboxedStuff"
>Z</a
> #) <a href="#" class="selflink"
>#</a
></p
></div
><div class="top"
><p class="src"
><a id="v:unboxedSum" class="def"
>unboxedSum</a
> :: (# <a href="#" title="UnboxedStuff"
>X</a
> | <a href="#" title="UnboxedStuff"
>Y</a
> #) -&gt; (# <a href="#" title="UnboxedStuff"
>X</a
> | <a href="#" title="UnboxedStuff"
>Y</a
> | <a href="#" title="UnboxedStuff"
>Z</a
> #) <a href="#" class="selflink"
>#</a
></p
></div
></div
></div
><div id="footer"
></div
></body
></html
>
18 changes: 18 additions & 0 deletions html-test/src/UnboxedStuff.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{-# LANGUAGE UnboxedSums, UnboxedTuples #-}
module UnboxedStuff where

data X
data Y
data Z

-- * Unboxed type constructors

unboxedUnit :: (# #) -> (# #)
unboxedUnit = undefined

unboxedTuple :: (# X, Y #) -> (# X, Y, Z #)
unboxedTuple = undefined

unboxedSum :: (# X | Y #) -> (# X | Y | Z #)
unboxedSum = undefined

36 changes: 36 additions & 0 deletions latex-test/ref/UnboxedStuff/UnboxedStuff.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
\haddockmoduleheading{UnboxedStuff}
\label{module:UnboxedStuff}
\haddockbeginheader
{\haddockverb\begin{verbatim}
module UnboxedStuff (
X, Y, Z, unboxedUnit, unboxedTuple, unboxedSum
) where\end{verbatim}}
\haddockendheader

\begin{haddockdesc}
\item[\begin{tabular}{@{}l}
data\ X
\end{tabular}]
\end{haddockdesc}
\begin{haddockdesc}
\item[\begin{tabular}{@{}l}
data\ Y
\end{tabular}]
\end{haddockdesc}
\begin{haddockdesc}
\item[\begin{tabular}{@{}l}
data\ Z
\end{tabular}]
\end{haddockdesc}
\section{Unboxed type constructors}
\begin{haddockdesc}
\item[
unboxedUnit\ ::\ ({\char '43}\ {\char '43})\ ->\ ({\char '43}\ {\char '43})
]
\item[
unboxedTuple\ ::\ ({\char '43}\ X,\ Y\ {\char '43})\ ->\ ({\char '43}\ X,\ Y,\ Z\ {\char '43})
]
\item[
unboxedSum\ ::\ ({\char '43}\ X\ |\ Y\ {\char '43})\ ->\ ({\char '43}\ X\ |\ Y\ |\ Z\ {\char '43})
]
\end{haddockdesc}
Loading