GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2023-10-30T10:35:14Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/19767GHC should generate workers with unlifted arguments for lifted types.2023-10-30T10:35:14ZAndreas KlebingerGHC should generate workers with unlifted arguments for lifted types.## Motivation
We already have good machinery to facilitate construction and deconstruction canceling out between caller and callee using WW.
I feel like we could reasonably expand this machinery to also facilitate to some degree avoidi...## Motivation
We already have good machinery to facilitate construction and deconstruction canceling out between caller and callee using WW.
I feel like we could reasonably expand this machinery to also facilitate to some degree avoiding re-evaluating/tag checks on values between caller and callee.
Currently if we have a silly function like this:
```haskell
{-# NOINLINE foo #-}
foo !x !y !z !n =
x || y || z || n > (1 :: Int)
```
It will evaluate x/x/z at **least** twice. Once for the seqs added by the bangs, and once for actual control flow. Tag inference will at least avoid evaluating these twice inside the body.
But what if the caller had already cased on the the arguments? Then we *still* recheck for the tag in the worker.
The worker currently looks like this:
```A.$wfoo
= \ (w_s1mU :: Bool)
(w1_s1mV :: Bool)
(w2_s1mW :: Bool)
(ww_s1n0 :: Int#) ->
case w1_s1mV of y_X1 { __DEFAULT ->
case w2_s1mW of z_X2 { __DEFAULT ->
case w_s1mU of {
False ->
case y_X1 of {
False ->
case z_X2 of {
False -> tagToEnum# @Bool (># ww_s1n0 1#);
True -> GHC.Types.True
};
True -> GHC.Types.True
};
True -> GHC.Types.True
}
}
}
```
But with [unlifted data types](https://gitlab.haskell.org/ghc/ghc/-/wikis/unlifted-data-types) we shouldn't need to do so.
It would be wonderful if we could instead generate a worker like this:
```haskell
A.$wfoo
= \ (w_s1mU :: unlifted Bool)
(y_X1 :: unlifted Bool)
(z_X2 :: unlifted Bool)
(ww_s1n0 :: Int#) ->
case lift w_s1mU of { -- lift to bring it back into the lifted domain,
-- but tag inference could still elid the tag check easily.
False ->
case lift y_X1 of {
False ->
case lift z_X2 of {
False -> tagToEnum# @Bool (># ww_s1n0 1#);
True -> GHC.Types.True
};
True -> GHC.Types.True
};
True -> GHC.Types.True
}
}
}
```
And the decision of weither or not a lifted argument could be driven by demand analysis much in the same way as we drive unboxing currently. Only it would naturally apply to sum types and avoid the risk of reboxing.
The wrapper would be a bigger problem. In the absence of changing Core (which might be reasonable) we would need something like this as a wrapper:
foo
= \ (w_s1mU :: Bool)
(w1_s1mV :: Bool)
(w2_s1mW :: Bool)
(w3_s1mX :: Int) ->
case seqCoerce# w_s1mU of u -> -- seqCoerce# would cancel out with seqs in the caller via tag inference during codegen.
case seqCoerce# w1_s1mV of v ->
case seqCoerce# w2_s1mW of w ->
case w3_s1mX of { I# ww1_s1n0 ->
A.$wfoo u v w ww1_s1n0
}
------
This is not meant as a concrete implementation proposal. But the idea of having WW passing arguments unlifted crossed my mind a lot during the work on tag inference. None of this needs to be exposed to users so I think it would be reasonable.
It's also a bit open how useful this would be. It obviously adds work for the compiler at the very least so it would need to pay it's dues. And with enough inlining there are very few boundries across which repeated seqs would cancel out to pay these.
However it would allow W/W to improve things where unboxing isn't an option. So hard to say without spending some time looking into this further. Which I don't plan to do in the near future.https://gitlab.haskell.org/ghc/ghc/-/issues/24108Add support for more LLVM targets2023-10-24T15:06:13ZglaubitzAdd support for more LLVM targets## Motivation
LLVM supports far more targets than the native code generator (NGC) available in GHC [1].
If GHC could be extended to provide LLVM backend support for LLVM targets such as M68k, MIPS, PowerPC and SPARC, it would for a fas...## Motivation
LLVM supports far more targets than the native code generator (NGC) available in GHC [1].
If GHC could be extended to provide LLVM backend support for LLVM targets such as M68k, MIPS, PowerPC and SPARC, it would for a faster compiler on these targets since LLVM-IR is more efficient than intermediate C code.
If LLVM support was available for 32-bit PowerPC, for example, it could easily be used to work around #23969.
## Proposal
Enable LLVM target support for M68k, MIPS, PowerPC and SPARC.
> [1] https://github.com/llvm/llvm-project/tree/main/llvm/lib/Targethttps://gitlab.haskell.org/ghc/ghc/-/issues/24060Pin the version of git used in Darwin CI2023-10-19T12:29:40ZBryan Rbryan@haskell.foundationPin the version of git used in Darwin CIFollowup to #24055.
On Darwin, `git` comes from the system and isn't pinned. Some versions of git seemed to have a bug that was the root cause of #24055. The bug was present in 2.40.1, but absent in 2.42.0.
Darwin CI should use a pinne...Followup to #24055.
On Darwin, `git` comes from the system and isn't pinned. Some versions of git seemed to have a bug that was the root cause of #24055. The bug was present in 2.40.1, but absent in 2.42.0.
Darwin CI should use a pinned, working version of git to prevent future surprises.https://gitlab.haskell.org/ghc/ghc/-/issues/21193Should evaluating a function return a tagged pointer.2023-10-04T03:37:31ZAndreas KlebingerShould evaluating a function return a tagged pointer.I've recently came across code like this. Not that this is from an unoptimized build and I think it's a rare issue in optimized builds.
```
case GHC.Utils.Outputable.ftext of conrep32_sPya [Occ=Once1] {
```
However the `conre...I've recently came across code like this. Not that this is from an unoptimized build and I think it's a rare issue in optimized builds.
```
case GHC.Utils.Outputable.ftext of conrep32_sPya [Occ=Once1] {
```
However the `conrep32_sPya` turned out to have no tag bits set despite being evaluated.
This was from a debug build so we had no LFInfo about `ftext` and therefore evaluated it via an unknown call.
```
R1 = GHC.Utils.Outputable.ftext_closure;
P64[Sp] = _sPy9::P64;
Sp = Sp - 8;
unwind Sp = Just Sp + 272;
call stg_ap_0_fast(R1) returns to c1jJX, args: 8, res: 8, upd: 8;
```
However this doesn't actually tag the result. Because stg_ap_0_fast ends up calling the ENTER macro which is defined such:
```
#define ENTER_(ret,x) \
again: \
W_ info; \
LOAD_INFO(ret,x) \
/* See Note [Heap memory barriers] in SMP.h */ \
prim_read_barrier; \
switch [INVALID_OBJECT .. N_CLOSURE_TYPES] \
(TO_W_( %INFO_TYPE(%STD_INFO(info)) )) { \
case \
IND, \
IND_STATIC: \
{ \
x = StgInd_indirectee(x); \
goto again; \
} \
case \
FUN, \
FUN_1_0, \
FUN_0_1, \
FUN_2_0, \
FUN_1_1, \
FUN_0_2, \
FUN_STATIC, \
BCO, \
PAP: \
{ \
ret(x); \
} \
default: \
{ \
x = UNTAG_IF_PROF(x); \
jump %ENTRY_CODE(info) (x); \
} \
}
```
That is for functions/paps we simply return the argument as-is.
--------------------
The obvious downside is that we are less likely to be able to tell the arity of functions without inspecting their info table. However it makes the "entry" code of functions really trivial.
If we were to change this we would merely have to extract the arity from the info table in the various FUN_* branches and tag the pointer with it.
However in an optimized build almost all references to functions should come into existence already tagged so I don't think this is likely to significantly change runtime performance.
Maybe I will look at changing this at some point but given it's low impact I don't think it's a priority.https://gitlab.haskell.org/ghc/ghc/-/issues/24026Core Lint error with -fdefer-type-errors and bad RULE2023-10-03T14:12:23ZKrzysztof GogolewskiCore Lint error with -fdefer-type-errors and bad RULETo reproduce:
```hs
module T24026 where
{-# RULES "f" forall (x :: Bool). f x = 0 #-}
f :: Int -> Int
f x = 0
```
```
$ ghc -dlint -fdefer-type-errors T24026
[1 of 1] Compiling T24026 ( T24026.hs, T24026.o )
*** Core Lint e...To reproduce:
```hs
module T24026 where
{-# RULES "f" forall (x :: Bool). f x = 0 #-}
f :: Int -> Int
f x = 0
```
```
$ ghc -dlint -fdefer-type-errors T24026
[1 of 1] Compiling T24026 ( T24026.hs, T24026.o )
*** Core Lint errors : in result of Desugar (before optimization) ***
T24026.hs:6:1: warning:
The coercion variable co_awP :: Bool ~# Int
[LclId[CoVarId]]
is out of scope
In the RHS of f :: Int -> Int
In a rule attached to f :: Int -> Int
Substitution: <InScope = {}
IdSubst = []
TvSubst = []
CvSubst = []>
*** Offending Program ***
Rec {
$trModule :: Module
[LclIdX]
$trModule = Module (TrNameS "main"#) (TrNameS "T24026"#)
f :: Int -> Int
[LclIdX,
RULES: "f" forall (x_awv :: Bool).
f (x_awv `cast` (Sub co_awP :: Bool ~R# Int))
= I# 0#]
f = \ (x_awu :: Int) -> I# 0#
end Rec }
````
The RULE has a type error, the coercion `Int ~ Bool` from `-fdefer-type-errors` is not bound.9.10.1https://gitlab.haskell.org/ghc/ghc/-/issues/23380Linear types: desugaring of mixed alternatives2023-09-26T01:13:09ZKrzysztof GogolewskiLinear types: desugaring of mixed alternativesThe following program fails a linearity check with `-dcore-lint`
```haskell
{-# LANGUAGE LinearTypes #-}
module M where
f :: Bool %1 -> Bool
f True = True
f x = x
```
This is best investigated after !10333, because it involves a DEFAU...The following program fails a linearity check with `-dcore-lint`
```haskell
{-# LANGUAGE LinearTypes #-}
module M where
f :: Bool %1 -> Bool
f True = True
f x = x
```
This is best investigated after !10333, because it involves a DEFAULT alternative, which is currently mishandled.https://gitlab.haskell.org/ghc/ghc/-/issues/9678Warning about __sync_fetch_and_nand semantics from gcc2023-09-21T07:15:21ZgintasWarning about __sync_fetch_and_nand semantics from gccWhen building ghc, I'm getting warnings about __sync_fetch_and_nand that the semantics have changed for gcc 4.4+. I'm building on Windows, but I don't think that matters.
Is this an indication of a problem or does that warning just need...When building ghc, I'm getting warnings about __sync_fetch_and_nand that the semantics have changed for gcc 4.4+. I'm building on Windows, but I don't think that matters.
Is this an indication of a problem or does that warning just need to be silenced?
I ran the build with gcc 4.8.3:
$ inplace/mingw/bin/gcc -v
Using built-in specs.
COLLECT_GCC=C:\\msys64\\home\\Gintas\\ghc\\inplace\\mingw\\bin\\gcc.exe
COLLECT_LTO_WRAPPER=C:/msys64/home/Gintas/ghc/inplace/mingw/bin/../libexec/gcc/x86_64-w64-mingw32/4.8.3/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-4.8.3/configure --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysroot=/c/mingw483/x86_64-483-posix-seh-rt_v3-rev0/mingw64 --with-gxx-include-dir=/mingw64/x86_64-w64-mingw32/include/c++ --enable-shared --enable-static --disable-multilib --enable-languages=ada,c,c++,fortran,objc,obj-c++,lto --enable-libstdcxx-time=yes --enable-threads=posix --enable-libgomp --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-isl-version-check --disable-cloog-version-check --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=nocona --with-tune=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --with-mpc=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --with-isl=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --with-cloog=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --enable-cloog-backend=isl --with-pkgversion='x86_64-posix-seh-rev0, Built by MinGW-W64 project' --with-bugurl=http://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -I/c/mingw483/x86_64-483-posix-seh-rt_v3-rev0/mingw64/opt/include -I/c/mingw483/prerequisites/x86_64-zlib-static/include -I/c/mingw483/prerequisites/x86_64-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -I/c/mingw483/x86_64-483-posix-seh-rt_v3-rev0/mingw64/opt/include -I/c/mingw483/prerequisites/x86_64-zlib-static/include -I/c/mingw483/prerequisites/x86_64-w64-mingw32-static/include' CPPFLAGS= LDFLAGS='-pipe -L/c/mingw483/x86_64-483-posix-seh-rt_v3-rev0/mingw64/opt/lib -L/c/mingw483/prerequisites/x86_64-zlib-static/lib -L/c/mingw483/prerequisites/x86_64-w64-mingw32-static/lib'
Thread model: posix
gcc version 4.8.3 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 7.9 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Build System |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Warning about __sync_fetch_and_nand semantics from gcc","status":"New","operating_system":"","component":"Build System","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.9","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"When building ghc, I'm getting warnings about __sync_fetch_and_nand that the semantics have changed for gcc 4.4+. I'm building on Windows, but I don't think that matters.\r\n\r\nIs this an indication of a problem or does that warning just need to be silenced?\r\n\r\nI ran the build with gcc 4.8.3:\r\n\r\n$ inplace/mingw/bin/gcc -v\r\nUsing built-in specs.\r\nCOLLECT_GCC=C:\\msys64\\home\\Gintas\\ghc\\inplace\\mingw\\bin\\gcc.exe\r\nCOLLECT_LTO_WRAPPER=C:/msys64/home/Gintas/ghc/inplace/mingw/bin/../libexec/gcc/x86_64-w64-mingw32/4.8.3/lto-wrapper.exe\r\nTarget: x86_64-w64-mingw32\r\nConfigured with: ../../../src/gcc-4.8.3/configure --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysroot=/c/mingw483/x86_64-483-posix-seh-rt_v3-rev0/mingw64 --with-gxx-include-dir=/mingw64/x86_64-w64-mingw32/include/c++ --enable-shared --enable-static --disable-multilib --enable-languages=ada,c,c++,fortran,objc,obj-c++,lto --enable-libstdcxx-time=yes --enable-threads=posix --enable-libgomp --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-isl-version-check --disable-cloog-version-check --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=nocona --with-tune=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --with-mpc=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --with-isl=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --with-cloog=/c/mingw483/prerequisites/x86_64-w64-mingw32-static --enable-cloog-backend=isl --with-pkgversion='x86_64-posix-seh-rev0, Built by MinGW-W64 project' --with-bugurl=http://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -I/c/mingw483/x86_64-483-posix-seh-rt_v3-rev0/mingw64/opt/include -I/c/mingw483/prerequisites/x86_64-zlib-static/include -I/c/mingw483/prerequisites/x86_64-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -I/c/mingw483/x86_64-483-posix-seh-rt_v3-rev0/mingw64/opt/include -I/c/mingw483/prerequisites/x86_64-zlib-static/include -I/c/mingw483/prerequisites/x86_64-w64-mingw32-static/include' CPPFLAGS= LDFLAGS='-pipe -L/c/mingw483/x86_64-483-posix-seh-rt_v3-rev0/mingw64/opt/lib -L/c/mingw483/prerequisites/x86_64-zlib-static/lib -L/c/mingw483/prerequisites/x86_64-w64-mingw32-static/lib'\r\nThread model: posix\r\ngcc version 4.8.3 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)\r\n","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/23956Undocumented infelicity: imported type operators can be used parenthesized wi...2023-09-19T15:34:12ZMarioUndocumented infelicity: imported type operators can be used parenthesized with no `NoTypeOperators`## Summary
The [Bug & Infelicities](https://downloads.haskell.org/ghc/latest/docs/users_guide/bugs.html) section of GHC user's guide doesn't hint at this little expansion of Haskell 2010 syntax:
```
{-# LANGUAGE Haskell2010, NoTypeOper...## Summary
The [Bug & Infelicities](https://downloads.haskell.org/ghc/latest/docs/users_guide/bugs.html) section of GHC user's guide doesn't hint at this little expansion of Haskell 2010 syntax:
```
{-# LANGUAGE Haskell2010, NoTypeOperators #-}
import Data.Type.Equality ((:~:))
type T1 a b = (:~:) a b
type T2 a b = a :~: b
```
The infelicity is that the `T1` type declaration is accepted even though the `TypeOperators` extension is turned off. This doesn't match the syntax of Haskell 2010, which doesn't allow type operators whether parenthesized or not. The `T2` declaration is correctly rejected by GHC.
## Proposed improvements or changes
This is technically a bug but it's not a new one -- GHC 8.4 behaves the same -- so I'm not suggesting we should change this behaviour, only document it in the Infelicities section.
The section on the [TypeOperators](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/type_operators.html) language extension could also use a bit of clarification. It currently states that
> The language `TypeOperators` allows you to use infix operators in types.
which is not quite true, because you can use an infix operator without the extension as long as it's in prefix position, parenthesized.
## Environment
* GHC version used (if appropriate): 6.29.10.1https://gitlab.haskell.org/ghc/ghc/-/issues/13011Simplifier ticks exhausted: a 10-line case2023-09-18T10:02:34ZL.K.RebellionSimplifier ticks exhausted: a 10-line caseI was trying to define a function that could take itself as an argument.
```hs
newtype MobiusFn a = MobiusFn {
func :: MobiusFn a -> a
}
spin :: MobiusFn a -> a
spin mf = func mf mf
```
Use it to find suffixes of a `String`
```hs...I was trying to define a function that could take itself as an argument.
```hs
newtype MobiusFn a = MobiusFn {
func :: MobiusFn a -> a
}
spin :: MobiusFn a -> a
spin mf = func mf mf
```
Use it to find suffixes of a `String`
```hs
suffixes :: String -> [String]
suffixes = spin $ MobiusFn suffixesMF
where suffixesMF _ [] = []
suffixesMF mf s@(_:xs) = s : spin mf xs
```
Add `main` and compile
```hs
main = readLn >>= (print . suffixes)
```
The compiler panicked with
```
[1 of 1] Compiling Main ( test.hs, test.o )
ghc: panic! (the 'impossible' happened)
(GHC version 8.0.1 for i386-unknown-linux):
Simplifier ticks exhausted
When trying UnfoldingDone mf_s1UD
```
But if we ignore the empty string case, it works well with non-empty strings.
```hs
suffixes :: String -> [String]
suffixes = spin $ MobiusFn suffixesMF
where suffixesMF _ s@[_] = [s]
suffixesMF mf s@(_:xs) = s : spin mf xs
```
Two versions tested.
```
GHC version 8.0.1 for i386-unknown-linux
GHC version 7.10.3 for i386-unknown-linux
```
Thank you for reading.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.0.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | high |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Simplifier ticks exhausted: a 10-line case","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.0.1","keywords":["Simplifier","exhausted","ticks"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"I was trying to define a function that could take itself as an argument.\r\n\r\n{{{#!hs\r\nnewtype MobiusFn a = MobiusFn {\r\n func :: MobiusFn a -> a\r\n}\r\n\r\nspin :: MobiusFn a -> a\r\nspin mf = func mf mf\r\n}}}\r\n\r\nUse it to find suffixes of a `String`\r\n\r\n{{{#!hs\r\nsuffixes :: String -> [String]\r\nsuffixes = spin $ MobiusFn suffixesMF\r\n where suffixesMF _ [] = []\r\n suffixesMF mf s@(_:xs) = s : spin mf xs\r\n}}}\r\n\r\nAdd `main` and compile\r\n{{{#!hs\r\nmain = readLn >>= (print . suffixes)\r\n}}}\r\n\r\nThe compiler panicked with \r\n{{{\r\n[1 of 1] Compiling Main ( test.hs, test.o )\r\nghc: panic! (the 'impossible' happened)\r\n (GHC version 8.0.1 for i386-unknown-linux):\r\n Simplifier ticks exhausted\r\n When trying UnfoldingDone mf_s1UD\r\n}}}\r\n\r\nBut if we ignore the empty string case, it works well with non-empty strings.\r\n\r\n{{{#!hs\r\nsuffixes :: String -> [String]\r\nsuffixes = spin $ MobiusFn suffixesMF\r\n where suffixesMF _ s@[_] = [s]\r\n suffixesMF mf s@(_:xs) = s : spin mf xs\r\n}}}\r\n\r\nTwo versions tested.\r\n{{{\r\nGHC version 8.0.1 for i386-unknown-linux\r\nGHC version 7.10.3 for i386-unknown-linux\r\n}}}\r\nThank you for reading.\r\n","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/23943Exploit unique pointers (project)2023-09-16T12:05:00ZSimon Peyton JonesExploit unique pointers (project)Here is a simple, folk-lore idea for reducing GC load in GHC, which I
discussed with Daan Leijen and Anton Lorenzen at ICFP'23. This ticket is just to record
the idea.
## The opportunity
* Claim: many data structures are allocated, co...Here is a simple, folk-lore idea for reducing GC load in GHC, which I
discussed with Daan Leijen and Anton Lorenzen at ICFP'23. This ticket is just to record
the idea.
## The opportunity
* Claim: many data structures are allocated, consumed, and discarded, *without ever being duplicated*. E.g. in `map f (reverse xs)`, the list produced by `reverse` is immediately consumed by `map`.
* A one-bit reference count on each heap object would suffice, in many cases, to tell us
at runtime when a heap object (such as the list returned by `reverse`) is uniquely referenced.
* When a function consumes a unique input object, two opportunities arise:
1. We could could return the cell to the storage manager.
2. We could immediately re-use the cell. For example, if `map` consumes a unique list, it can allocate the returned cells in the input cells. See Daan Leijen's ICFP '23 paper [Fully in place functional programming](https://dl.acm.org/doi/10.1145/3607840). NB that paper describes something more ambitious than I am describing here. But the immediate-reuse stuff is relevant, and the paper has a number of compelling examples.
* If a uniquely-referenced thunk is entered, it doesn't need to be updated. That could be another saving.
* The garbage collector still runs un-modified, just as now. So we will still recover cycles, cells used more than once, etc.
* Even the reference count on an object says "many", at GC time there might now be only one pointer to it. So the GC (which traces all references) can restore unique-pointer information.
## Reflecting on the opportunity
* A one-bit reference count means that an object is either "unique" or "many". As soon as it goes to "many" we lose track of the count, so it can never go back to "unique" (except by the action of the GC). So this plan will only be effective if many objects are born and die without every having a shared pointer to them. Before investing much we'd need data on how common that is. (My intuition is that it'd be very common.)
Of course you could always have a 2-bit or 3-bit ref count instead.
* Opportunity (1) is to give back a freed object to the storage manager.
But because these freed objects are scattered, we can't use bump-pointer allocation for them.
However, especially with the non-moving collection, old generations use free-lists and so
may just possibly exploit such freed objects. But my guess is that (1) is of marginal
importance, and that (2) is the real win.
* Note that there extra run-time tests (and extra code) to check for uniqueness. Binary sizes will increase; don't know how much.
* GHC currently does *one* bump-pointer heap check to cover *multiple* allocations. It's not clear how to adapt this if some, but not all, of those allocations are being done in-place instead.
* When would this be a win? It depends on whether uniquely-referenced data is common in practice. we need data! And we could perhaps gather that data *without* (yet) exploiting. (Much of the implementation cost is associated with the exploitation, I think, so gathering the data might take a lot less effort.
* When a unique heap object is consumed, we may re-use it. But if it is in the old generation, reusing it would mean adding lots of pointers to the remembered set (pointers into the young generation). That's just as expensive as allocation! So it probably does not make sense to re-use old-generation objects.
* Where could we store a 1-bit reference count? Two thoughts
* In the least-significant bit of the info pointer. We'd need to mask it before entering it.
* In the *pointer* to the object. E.g. a unique pointer has LS bit=0, a shared pointer has
LS bit=1. Messing with the pointer would avoid memory traffic (good). But:
* We use those same bits for pointer tagging, so we'd have to steal one back for this sharing purpose.
* It would be harder for the GC to restore unique-pointer info. I think much harder.
## What next?
Exploring this idea could be a very interesting project, but it would
be a non-trivial amount of work.
The first thing would be to gather data about how often unique pointeres are encountered in practice.https://gitlab.haskell.org/ghc/ghc/-/issues/12203Allow constructors on LHS of (implicit) bidirectional pattern synonym2023-09-15T00:31:43ZEdward Z. YangAllow constructors on LHS of (implicit) bidirectional pattern synonymInspired by the recent Ghostbuster paper http://www.cs.ox.ac.uk/people/timothy.zakian/ghostbuster.pdf , I was experimenting with using pattern synonyms to simulate GADTs. In the process, I noticed that we should be able to generalize the...Inspired by the recent Ghostbuster paper http://www.cs.ox.ac.uk/people/timothy.zakian/ghostbuster.pdf , I was experimenting with using pattern synonyms to simulate GADTs. In the process, I noticed that we should be able to generalize the implicit bidirectional synonyms to allow constructors on the LHS.
Consider this program:
```
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
module GhostBuster where
import GHC.TypeLits
newtype Vec a (n :: Nat) = Vec { unVec :: [a] }
pattern VNil :: Vec a 0
pattern VNil = Vec []
pattern VCons :: a -> Vec a n -> Vec a (n + 1)
pattern VCons x xs <- Vec (x : (Vec -> xs)) where
VCons x (Vec xs) = Vec (x : xs)
headVec :: Vec a (n + 1) -> a
headVec (VCons x _) = x
```
In effect, we simulate a vector GADT using pattern synonyms to handle the newtype `Vec`.
I would like it if I could specify the `VCons` pattern more simply, as just:
```
pattern VCons :: a -> Vec a n -> Vec a (n + 1)
pattern VCons x (Vec xs) = Vec (x : xs)
```
And GHC would infer the correct pattern (the constructor can be read off immediately), automatically replacing occurrences of `xs` with a view pattern that reconstructs the constructors that were stripped off on the LHS.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 8.0.1 |
| Type | FeatureRequest |
| TypeOfFailure | OtherFailure |
| Priority | low |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Allow constructors on LHS of (implicit) bidirectional pattern synonym","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.0.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"FeatureRequest","description":"Inspired by the recent Ghostbuster paper http://www.cs.ox.ac.uk/people/timothy.zakian/ghostbuster.pdf , I was experimenting with using pattern synonyms to simulate GADTs. In the process, I noticed that we should be able to generalize the implicit bidirectional synonyms to allow constructors on the LHS.\r\n\r\nConsider this program:\r\n\r\n{{{\r\n{-# LANGUAGE KindSignatures #-}\r\n{-# LANGUAGE DataKinds #-}\r\n{-# LANGUAGE PatternSynonyms #-}\r\n{-# LANGUAGE ViewPatterns #-}\r\n{-# LANGUAGE TypeOperators #-}\r\n{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}\r\nmodule GhostBuster where\r\n\r\nimport GHC.TypeLits\r\n\r\nnewtype Vec a (n :: Nat) = Vec { unVec :: [a] }\r\n\r\npattern VNil :: Vec a 0\r\npattern VNil = Vec []\r\n\r\npattern VCons :: a -> Vec a n -> Vec a (n + 1)\r\npattern VCons x xs <- Vec (x : (Vec -> xs)) where\r\n VCons x (Vec xs) = Vec (x : xs)\r\n\r\nheadVec :: Vec a (n + 1) -> a\r\nheadVec (VCons x _) = x\r\n}}}\r\n\r\nIn effect, we simulate a vector GADT using pattern synonyms to handle the newtype `Vec`.\r\n\r\nI would like it if I could specify the `VCons` pattern more simply, as just:\r\n\r\n{{{\r\npattern VCons :: a -> Vec a n -> Vec a (n + 1)\r\npattern VCons x (Vec xs) = Vec (x : xs)\r\n}}}\r\n\r\nAnd GHC would infer the correct pattern (the constructor can be read off immediately), automatically replacing occurrences of `xs` with a view pattern that reconstructs the constructors that were stripped off on the LHS.","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/23935Empty Haddock comments no longer occur in the AST as `HsDoc`2023-09-14T19:20:21ZamesgenEmpty Haddock comments no longer occur in the AST as `HsDoc`## Summary
Consider the following two type signatures.
```haskell
foo :: {- |-} A -> B
bar :: {- | -} A -> B
```
Comparing the AST (with `-haddock`) of `foo` and `bar`, note that `foo` does not contain a `HsDoc` (searchf or `WithHsDocId...## Summary
Consider the following two type signatures.
```haskell
foo :: {- |-} A -> B
bar :: {- | -} A -> B
```
Comparing the AST (with `-haddock`) of `foo` and `bar`, note that `foo` does not contain a `HsDoc` (searchf or `WithHsDocIdentifiers`), but `bar` does:
<table>
<tr><th>
`foo`</th><th>
`bar`</th></tr>
<tr>
<td>
```haskell
(L
(SrcSpanAnn (EpAnn
(Anchor
{ <interactive>:1:1-20 }
(UnchangedAnchor))
(AnnListItem
[])
(EpaComments
[])) { <interactive>:1:1-20 })
(SigD
(NoExtField)
(TypeSig
(EpAnn
(Anchor
{ <interactive>:1:1-3 }
(UnchangedAnchor))
(AnnSig
(AddEpAnn AnnDcolon (EpaSpan { <interactive>:1:5-6 }))
[])
(EpaComments
[]))
[(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:1-3 })
(Unqual
{OccName: foo}))]
(HsWC
(NoExtField)
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:15-20 })
(HsSig
(NoExtField)
(HsOuterImplicit
(NoExtField))
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:15-20 })
(HsFunTy
(EpAnn
(Anchor
{ <interactive>:1:15 }
(UnchangedAnchor))
(NoEpAnns)
(EpaComments
[]))
(HsUnrestrictedArrow
(L
(TokenLoc
(EpaSpan { <interactive>:1:17-18 }))
(HsNormalTok)))
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:15 })
(HsTyVar
(EpAnn
(Anchor
{ <interactive>:1:15 }
(UnchangedAnchor))
[]
(EpaComments
[]))
(NotPromoted)
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:15 })
(Unqual
{OccName: A}))))
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:20 })
(HsTyVar
(EpAnn
(Anchor
{ <interactive>:1:20 }
(UnchangedAnchor))
[]
(EpaComments
[]))
(NotPromoted)
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:20 })
(Unqual
{OccName: B}))))))))))))
```
</td>
<td>
```haskell
(L
(SrcSpanAnn (EpAnn
(Anchor
{ <interactive>:1:1-21 }
(UnchangedAnchor))
(AnnListItem
[])
(EpaComments
[])) { <interactive>:1:1-21 })
(SigD
(NoExtField)
(TypeSig
(EpAnn
(Anchor
{ <interactive>:1:1-3 }
(UnchangedAnchor))
(AnnSig
(AddEpAnn AnnDcolon (EpaSpan { <interactive>:1:5-6 }))
[])
(EpaComments
[]))
[(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:1-3 })
(Unqual
{OccName: bar}))]
(HsWC
(NoExtField)
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:16-21 })
(HsSig
(NoExtField)
(HsOuterImplicit
(NoExtField))
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:16-21 })
(HsFunTy
(EpAnn
(Anchor
{ <interactive>:1:16 }
(UnchangedAnchor))
(NoEpAnns)
(EpaComments
[]))
(HsUnrestrictedArrow
(L
(TokenLoc
(EpaSpan { <interactive>:1:18-19 }))
(HsNormalTok)))
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:16 })
(HsDocTy
(EpAnnNotUsed)
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:16 })
(HsTyVar
(EpAnn
(Anchor
{ <interactive>:1:16 }
(UnchangedAnchor))
[]
(EpaComments
[]))
(NotPromoted)
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:16 })
(Unqual
{OccName: A}))))
(L
{ <interactive>:1:8-14 }
(WithHsDocIdentifiers
(NestedDocString
(HsDocStringNext)
(L
{ <interactive>:1:8-14 }
(HsDocStringChunk
" ")))
[]))))
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:21 })
(HsTyVar
(EpAnn
(Anchor
{ <interactive>:1:21 }
(UnchangedAnchor))
[]
(EpaComments
[]))
(NotPromoted)
(L
(SrcSpanAnn (EpAnnNotUsed) { <interactive>:1:21 })
(Unqual
{OccName: B}))))))))))))
```
</td>
</tr>
</table>
Is there a particular reason for this? In GHC 8.10, the AST contained Haddock comments in both cases.
Concrete effects of this behavior:
- It makes the job of formatters like Ormolu (see issues [1068](https://github.com/tweag/ormolu/pull/1068), [1065](https://github.com/tweag/ormolu/issues/1065), [726](https://github.com/tweag/ormolu/issues/726)) that check of AST discrepancies automatically harder than necessary, as eg a natural rewrite from
```haskell
foo ::
-- |
--
A ->
B
```
to
```haskell
foo ::
-- |
A ->
B
```
contains a Haddock comment in the AST in the first snippet, but not in the second.
- A nice Haddock trick by @tomjaguarpaw1 ([blog post](http://h2.jaguarpaw.co.uk/posts/improving-the-typed-process-documentation/), search for "Forced type signatures to wrap") does [no longer work](https://github.com/tweag/ormolu/pull/1068#issuecomment-1707237587).
Ideally, the behavior would be changed as it was in 8.10; I could try to do that in case this behavior is not intentional.
## Environment
* GHC version used: Any GHC since 9.0 (I think this change is due to !2377)https://gitlab.haskell.org/ghc/ghc/-/issues/23838Allow a BUILD different from HOST2023-09-14T09:04:39ZRodrigo MesquitaAllow a BUILD different from HOSTCurrently, we require that the system building the compiler (BUILD) is the same system running the compiler (HOST), but allow the target of the compiler (TARGET) to be different from the BUILD/HOST.
* Why do we have this limitation in p...Currently, we require that the system building the compiler (BUILD) is the same system running the compiler (HOST), but allow the target of the compiler (TARGET) to be different from the BUILD/HOST.
* Why do we have this limitation in place?
* What do we need to do to lift it?
With the recent advances in the staged build configuration (namely, the `ghc-toolchain` `Target` files) lifting this restriction is likely simpler now.https://gitlab.haskell.org/ghc/ghc/-/issues/22034slow validate failure: GivenCheckSwap in hpc way2023-09-07T10:54:55ZMatthew Pickeringslow validate failure: GivenCheckSwap in hpc wayReproduce with
```
hadrian/build test --freeze1 --docs=none --flavour=slow-validate --test-speed=slow --only="GivenCheckSwap"
```
Looks like something needs to look through ticks.
```
-
-GivenCheckSwap.hs:11:9: warning: [-Woverlappin...Reproduce with
```
hadrian/build test --freeze1 --docs=none --flavour=slow-validate --test-speed=slow --only="GivenCheckSwap"
```
Looks like something needs to look through ticks.
```
-
-GivenCheckSwap.hs:11:9: warning: [-Woverlapping-patterns (in -Wdefault)]
- Pattern match is redundant
- In an equation for āgā: g y | False = ...
*** unexpected failure for GivenCheckSwap(hpc)
```Matthew PickeringMatthew Pickeringhttps://gitlab.haskell.org/ghc/ghc/-/issues/23910Replace absent lazy function arguments with rubbishLit even without W/W.2023-09-05T14:21:36ZAndreas KlebingerReplace absent lazy function arguments with rubbishLit even without W/W.Currently for something like `const` (assuming it isn't inlined) we often see code like this:
```haskell
foo x y z = .... const x y ....
```
W/W would iirc transform this into:
```haskell
foo x y z = $wfoo x z
$wfoo = ... const x <...Currently for something like `const` (assuming it isn't inlined) we often see code like this:
```haskell
foo x y z = .... const x y ....
```
W/W would iirc transform this into:
```haskell
foo x y z = $wfoo x z
$wfoo = ... const x <rubbishLit> ...
```
Which means y` isn't unnecessarily retained. However there seems little reason to limit this to worker wrapper.
Even without worker wrapper if we have
```haskell
foo x y z = .... const x y ....
```
it should be fine to rewrite this into:
```haskell
foo x y z = .... const x <rubbishLit> ....
```
It's possible I'm missing something but I think that should be fine and allow us to gc `y` as soon as we entered `foo`.https://gitlab.haskell.org/ghc/ghc/-/issues/20883Different -Woverlapping-patterns warnings in GHCi versus compiled code2023-08-31T13:05:37ZRyan ScottDifferent -Woverlapping-patterns warnings in GHCi versus compiled codeIn the following code:
```hs
{-# OPTIONS_GHC -Woverlapping-patterns #-}
module Bug where
f :: Int
f | id (id True)
= 1
| id (id True)
, True
= 2
| otherwise
= 3
```
The second guard is definitely redundant, and GHC 9.0.1...In the following code:
```hs
{-# OPTIONS_GHC -Woverlapping-patterns #-}
module Bug where
f :: Int
f | id (id True)
= 1
| id (id True)
, True
= 2
| otherwise
= 3
```
The second guard is definitely redundant, and GHC 9.0.1 and later recognize this when compiled normally:
```
$ ghc-9.0.1 Bug.hs -fforce-recomp
[1 of 1] Compiling Bug ( Bug.hs, Bug.o )
Bug.hs:8:5: warning: [-Woverlapping-patterns]
Pattern match is redundant
In an equation for āfā: f | id (id True), True = ...
|
8 | | id (id True)
| ^^^^^^^^^^^^
```
When loaded into GHCi, however, the warning does not occur for some reason:
```
$ ghci-9.0.1 Bug.hs
GHCi, version 9.0.1: https://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /home/rscott/.ghci
[1 of 1] Compiling Bug ( Bug.hs, interpreted )
Ok, one module loaded.
```
I would expect both modes to produce the same warnings.
cc @sgraf812https://gitlab.haskell.org/ghc/ghc/-/issues/23902:sprint in GHCi yields surprising results on BCOs linked as a group2023-08-29T14:22:08ZJaro Reinders:sprint in GHCi yields surprising results on BCOs linked as a group## Summary
When two bindings are defined in the same binding group their thunks will not update correctly.
## Steps to reproduce
```
GHCi, version 9.6.2: https://www.haskell.org/ghc/ :? for help
ghci> :{
ghci| y = 1 + 2 + 3 :: Int
g...## Summary
When two bindings are defined in the same binding group their thunks will not update correctly.
## Steps to reproduce
```
GHCi, version 9.6.2: https://www.haskell.org/ghc/ :? for help
ghci> :{
ghci| y = 1 + 2 + 3 :: Int
ghci| x = (y, y)
ghci| :}
ghci> :force y
y = 6
ghci> :sprint x
x = _
```
Notes:
* `:force` is not necessary, forcing `y` by printing it also does not update `x`.
* `;` instead of the multiline `:{` `:}` block also exhibits the same bad behaviour.
* Loading the `x` and `y` bindings from a file exhibits the same issue.
* You could argue that `x` itself simply hasn't evaluated the outer `(,)` constructor yet, but that doesn't explain this:
```
ghci> x = 1 + 2 :: Int; y = [x, x]
ghci> length y
2
ghci> :force x
x = 3
ghci> :sprint y
y = [_,_]
```
* `performMajorGC` does not change anything.
## Expected behavior
It should behave the same as two separate bindings:
```
ghci> y = 1 + 2 + 3 :: Int
ghci> x = (y, y)
ghci> :force y
y = 6
ghci> :sprint x
x = (6,6)
```
## Environment
* GHC version used: 9.6.2https://gitlab.haskell.org/ghc/ghc/-/issues/23900Change mtv_info to a set of MetaInfo2023-08-28T19:29:41Zsheafsam.derbyshire@gmail.comChange mtv_info to a set of MetaInfoCurrently, metavariables can have only one `MetaInfo`:
```haskell
data TcTyVarDetails
= SkolemTv {..}
| RuntimeUnk
| MetaTv { mtv_info :: MetaInfo
, mtv_ref :: IORef MetaDetails
, mtv_tclvl :: TcLevel }
d...Currently, metavariables can have only one `MetaInfo`:
```haskell
data TcTyVarDetails
= SkolemTv {..}
| RuntimeUnk
| MetaTv { mtv_info :: MetaInfo
, mtv_ref :: IORef MetaDetails
, mtv_tclvl :: TcLevel }
data MetaInfo
= TauTv
| TyVarTv
| RuntimeUnkTv
| CycleBreakerTv
| ConcreteTv ConcreteTvOrigin
```
This assumes that the concerns are orthogonal, for example that we never want a `TyVarTv` that is also a `CycleBreakerTv`, nor a `ConcreteTv` that is also a `TyVarTv`.
I don't know of any bugs in the compiler due to this design (which is why I marked this ticket as having low priority), but I think the design should instead be:
```haskell
data TcTyVarDetails
= ..
| MetaTv { mtv_infos :: [MetaInfo], .. }
data MetaInfo
= TyVarTv
| RuntimeUnkTv
| CycleBreakerTv
| ConcreteTv ConcreteTvOrigin
```
That is, store a `[MetaInfo]` in `MetaTv`, and remove the `TauTv` constructor of `MetaInfo` (it corresponds to a plain metavariable, i.e. `mtv_infos = []`).https://gitlab.haskell.org/ghc/ghc/-/issues/19605GHC2021 and Safe cause a warning2023-08-24T13:48:45ZOleg GrenrusGHC2021 and Safe cause a warning```haskell
{-# LANGUAGE GHC2021 #-} -- can be omitted
{-# LANGUAGE Safe #-}
module Example where
```
Causes a confusing, non-ignorable warning (i.e. `-Werror` will always fail the builds)
```
% /code/ghc/_buildstage1/bin/ghc -c Example...```haskell
{-# LANGUAGE GHC2021 #-} -- can be omitted
{-# LANGUAGE Safe #-}
module Example where
```
Causes a confusing, non-ignorable warning (i.e. `-Werror` will always fail the builds)
```
% /code/ghc/_buildstage1/bin/ghc -c Example.hs
<no location info>: warning:
-XGeneralizedNewtypeDeriving is not allowed in Safe Haskell; ignoring -XGeneralizedNewtypeDeriving
```Jaro ReindersJaro Reindershttps://gitlab.haskell.org/ghc/ghc/-/issues/23704Visible forall in pattern synonyms2023-08-23T17:14:43ZVladislav ZavialovVisible forall in pattern synonymsGHC Proposals [#281](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0281-visible-forall.rst) and [#402](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0402-gadt-syntax.rst) allow the use of vis...GHC Proposals [#281](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0281-visible-forall.rst) and [#402](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0402-gadt-syntax.rst) allow the use of visible forall in data constructors. That is, we should be able to write
```haskell
data T a where
MkT :: forall a -> a -> T a
```
A quite relevant ticket that also mentions this is #18389. But what about pattern synonyms?
```haskell
pattern P :: forall a -> a -> T a
pattern P a x = MkT a x
```
The interaction between `PatternSynonyms` and `RequiredTypeArguments` simply hasn't been discussed or specified anywhere.
It's a feature that makes sense. If we are able to define data constructor `MkT`, then we should also be able to define a synonym for it. I don't see any obstacles to implementing this. However, it would be nice to have a real world use case before putting effort into it.