Skip to content

Commit

Permalink
containsTypeFamilies: Don't crash if reification fails
Browse files Browse the repository at this point in the history
Instead of crashing when `reify` fails, we recover and conservatively assume
that the type does not contain any type families.

Fixes #1032.
  • Loading branch information
RyanGlScott committed Jun 7, 2023
1 parent edc1465 commit f518d33
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.markdown
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
next [????.??.??]
-----------------
* Fix a bug in which the `declare*` Template Haskell functions would fail if a
data type's field has a type that is defined in the same Template Haskell
quotation.

5.2.2 [2023.03.18]
------------------
* Fix a bug in which calling `ix i` (where `i` is a negative number) on `Text`
Expand Down
11 changes: 10 additions & 1 deletion src/Control/Lens/Internal/FieldTH.hs
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,16 @@ makeFieldInstance defType className decs =

containsTypeFamilies = go <=< D.resolveTypeSynonyms
where
go (ConT nm) = has (_FamilyI . _1 . _TypeFamilyD) <$> reify nm
go :: Type -> Q Bool
go (ConT nm) =
-- Note that the call to `reify` can fail if `nm` is not yet defined.
-- (This can actually happen if `nm` is declared in a Template Haskell
-- quote.) If this fails, there is no way to tell if the type contains
-- type families, so we recover and conservatively assume that is does not
-- contain any.
recover
(pure False)
(has (_FamilyI . _1 . _TypeFamilyD) <$> reify nm)
go ty = or <$> traverse go (ty ^.. plate)

-- We want to catch type families, but not *data* families. See #799.
Expand Down
7 changes: 7 additions & 0 deletions tests/templates.hs
Original file line number Diff line number Diff line change
Expand Up @@ -468,5 +468,12 @@ $(declareWrapped [d|
newtype instance T997FamB b = MkT997FamB b
|])

-- Ensure that a data type defined in a TH quote can have a field whose type
-- references another data type defined in the same quote (#1032)
declareFields [d|
data T1032A = T1032A { t1032ASubB :: T1032B }
data T1032B = T1032B { t1032BB :: Int }
|]

main :: IO ()
main = putStrLn "test/templates.hs: ok"

0 comments on commit f518d33

Please sign in to comment.