Skip to content
This repository was archived by the owner on Nov 1, 2018. It is now read-only.

Commit 149d791

Browse files
Simon Peyton Jonesbgamari
authored andcommitted
Fix error recovery for pattern synonyms
As Trac #15289 showed, we were carrying on after a type error in a pattern synonym, and then crashing. This patch improves error handling for pattern synonyms. I also moved a bit of code from TcBinds into TcPatSyn, which helpfully narrows the API. (cherry picked from commit 2896082)
1 parent 4cfeca0 commit 149d791

File tree

5 files changed

+95
-37
lines changed

5 files changed

+95
-37
lines changed

compiler/typecheck/TcBinds.hs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ import GhcPrelude
1919

2020
import {-# SOURCE #-} TcMatches ( tcGRHSsPat, tcMatchesFun )
2121
import {-# SOURCE #-} TcExpr ( tcMonoExpr )
22-
import {-# SOURCE #-} TcPatSyn ( tcInferPatSynDecl, tcCheckPatSynDecl
23-
, tcPatSynBuilderBind )
22+
import {-# SOURCE #-} TcPatSyn ( tcPatSynDecl, tcPatSynBuilderBind )
2423
import CoreSyn (Tickish (..))
2524
import CostCentre (mkUserCC, CCFlavour(DeclCC))
2625
import DynFlags
@@ -526,16 +525,10 @@ tc_single :: forall thing.
526525
tc_single _top_lvl sig_fn _prag_fn
527526
(L _ (PatSynBind _ psb@PSB{ psb_id = L _ name }))
528527
_ thing_inside
529-
= do { (aux_binds, tcg_env) <- tc_pat_syn_decl
528+
= do { (aux_binds, tcg_env) <- tcPatSynDecl psb (sig_fn name)
530529
; thing <- setGblEnv tcg_env thing_inside
531530
; return (aux_binds, thing)
532531
}
533-
where
534-
tc_pat_syn_decl :: TcM (LHsBinds GhcTcId, TcGblEnv)
535-
tc_pat_syn_decl = case sig_fn name of
536-
Nothing -> tcInferPatSynDecl psb
537-
Just (TcPatSynSig tpsi) -> tcCheckPatSynDecl psb tpsi
538-
Just _ -> panic "tc_single"
539532

540533
tc_single top_lvl sig_fn prag_fn lbind closed thing_inside
541534
= do { (binds1, ids) <- tcPolyBinds sig_fn prag_fn

compiler/typecheck/TcPatSyn.hs

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
{-# LANGUAGE FlexibleContexts #-}
1010
{-# LANGUAGE TypeFamilies #-}
1111

12-
module TcPatSyn ( tcInferPatSynDecl, tcCheckPatSynDecl
13-
, tcPatSynBuilderBind, tcPatSynBuilderOcc, nonBidirectionalErr
12+
module TcPatSyn ( tcPatSynDecl, tcPatSynBuilderBind
13+
, tcPatSynBuilderOcc, nonBidirectionalErr
1414
) where
1515

1616
import GhcPrelude
@@ -53,7 +53,7 @@ import FieldLabel
5353
import Bag
5454
import Util
5555
import ErrUtils
56-
import Control.Monad ( zipWithM )
56+
import Control.Monad ( zipWithM, when )
5757
import Data.List( partition )
5858

5959
#include "HsVersions.h"
@@ -66,6 +66,58 @@ import Data.List( partition )
6666
************************************************************************
6767
-}
6868

69+
tcPatSynDecl :: PatSynBind GhcRn GhcRn
70+
-> Maybe TcSigInfo
71+
-> TcM (LHsBinds GhcTc, TcGblEnv)
72+
tcPatSynDecl psb@(PSB { psb_id = L _ name, psb_args = details }) mb_sig
73+
= recoverM recover $
74+
case mb_sig of
75+
Nothing -> tcInferPatSynDecl psb
76+
Just (TcPatSynSig tpsi) -> tcCheckPatSynDecl psb tpsi
77+
_ -> panic "tcPatSynDecl"
78+
79+
where
80+
-- See Note [Pattern synonym error recovery]
81+
recover = do { matcher_name <- newImplicitBinder name mkMatcherOcc
82+
; let placeholder = AConLike $ PatSynCon $
83+
mk_placeholder matcher_name
84+
; gbl_env <- tcExtendGlobalEnv [placeholder] getGblEnv
85+
; return (emptyBag, gbl_env) }
86+
87+
(_arg_names, _rec_fields, is_infix) = collectPatSynArgInfo details
88+
mk_placeholder matcher_name
89+
= mkPatSyn name is_infix
90+
([mkTyVarBinder Specified alphaTyVar], []) ([], [])
91+
[] -- Arg tys
92+
alphaTy
93+
(matcher_id, True) Nothing
94+
[] -- Field labels
95+
where
96+
-- The matcher_id is used only by the desugarer, so actually
97+
-- and error-thunk would probably do just as well here.
98+
matcher_id = mkLocalId matcher_name $
99+
mkSpecForAllTys [alphaTyVar] alphaTy
100+
101+
tcPatSynDecl (XPatSynBind {}) _ = panic "tcPatSynDecl"
102+
103+
{- Note [Pattern synonym error recovery]
104+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105+
If type inference for a pattern synonym fails , we can't continue with
106+
the rest of tc_patsyn_finish, because we may get knock-on errors, or
107+
even a crash. E.g. from
108+
pattern What = True :: Maybe
109+
we get a kind error; and we must stop right away (Trac #15289).
110+
Hence the 'when insoluble failM' in tcInferPatSyn.
111+
112+
But does that abort compilation entirely? No -- we can recover
113+
and carry on, just as we do for value bindings, provided we plug in
114+
placeholder for the pattern synonym. The goal of the placeholder
115+
is not to cause a raft of follow-on errors. I've used the simplest
116+
thing for now, but we might need to elaborate it a bit later. (e.g.
117+
I've given it zero args, which may cause knock-on errors if it is
118+
used in a pattern.) But it'll do for now.
119+
-}
120+
69121
tcInferPatSynDecl :: PatSynBind GhcRn GhcRn
70122
-> TcM (LHsBinds GhcTc, TcGblEnv)
71123
tcInferPatSynDecl PSB{ psb_id = lname@(L _ name), psb_args = details,
@@ -76,14 +128,19 @@ tcInferPatSynDecl PSB{ psb_id = lname@(L _ name), psb_args = details,
76128
; let (arg_names, rec_fields, is_infix) = collectPatSynArgInfo details
77129
; (tclvl, wanted, ((lpat', args), pat_ty))
78130
<- pushLevelAndCaptureConstraints $
79-
tcInferNoInst $ \ exp_ty ->
80-
tcPat PatSyn lpat exp_ty $
131+
tcInferNoInst $ \ exp_ty ->
132+
tcPat PatSyn lpat exp_ty $
81133
mapM tcLookupId arg_names
82134

83135
; let named_taus = (name, pat_ty) : map (\arg -> (getName arg, varType arg)) args
84136

85-
; (qtvs, req_dicts, ev_binds, _) <- simplifyInfer tclvl NoRestrictions []
86-
named_taus wanted
137+
; (qtvs, req_dicts, ev_binds, insoluble)
138+
<- simplifyInfer tclvl NoRestrictions [] named_taus wanted
139+
140+
; when insoluble failM
141+
-- simplifyInfer doesn't fail if there are errors. But to avoid
142+
-- knock-on errors, or even crashes, we want to stop here.
143+
-- See Note [Pattern synonym error recovery]
87144

88145
; let (ex_tvs, prov_dicts) = tcCollectEx lpat'
89146
ex_tv_set = mkVarSet ex_tvs
@@ -772,10 +829,15 @@ tcPatSynBuilderBind (PSB { psb_id = L loc name, psb_def = lpat
772829

773830
| Right match_group <- mb_match_group -- Bidirectional
774831
= do { patsyn <- tcLookupPatSyn name
775-
; let Just (builder_id, need_dummy_arg) = patSynBuilder patsyn
776-
-- Bidirectional, so patSynBuilder returns Just
777-
778-
match_group' | need_dummy_arg = add_dummy_arg match_group
832+
; case patSynBuilder patsyn of {
833+
Nothing -> return emptyBag ;
834+
-- This case happens if we found a type error in the
835+
-- pattern synonym, recovered, and put a placeholder
836+
-- with patSynBuilder=Nothing in the environment
837+
838+
Just (builder_id, need_dummy_arg) -> -- Normal case
839+
do { -- Bidirectional, so patSynBuilder returns Just
840+
let match_group' | need_dummy_arg = add_dummy_arg match_group
779841
| otherwise = match_group
780842

781843
bind = FunBind { fun_ext = placeHolderNamesTc
@@ -790,7 +852,7 @@ tcPatSynBuilderBind (PSB { psb_id = L loc name, psb_def = lpat
790852
ppr patsyn $$ ppr builder_id <+> dcolon <+> ppr (idType builder_id)
791853
; (builder_binds, _) <- tcPolyCheck emptyPragEnv sig (noLoc bind)
792854
; traceTc "tcPatSynBuilderBind }" $ ppr builder_binds
793-
; return builder_binds }
855+
; return builder_binds } } }
794856

795857
| otherwise = panic "tcPatSynBuilderBind" -- Both cases dealt with
796858
where

compiler/typecheck/TcPatSyn.hs-boot

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
module TcPatSyn where
22

33
import HsSyn ( PatSynBind, LHsBinds )
4-
import TcRnTypes ( TcM, TcPatSynInfo )
4+
import TcRnTypes ( TcM, TcSigInfo )
55
import TcRnMonad ( TcGblEnv)
66
import Outputable ( Outputable )
77
import HsExtension ( GhcRn, GhcTc )
8+
import Data.Maybe ( Maybe )
89

9-
tcInferPatSynDecl :: PatSynBind GhcRn GhcRn
10-
-> TcM (LHsBinds GhcTc, TcGblEnv)
11-
12-
tcCheckPatSynDecl :: PatSynBind GhcRn GhcRn
13-
-> TcPatSynInfo
14-
-> TcM (LHsBinds GhcTc, TcGblEnv)
10+
tcPatSynDecl :: PatSynBind GhcRn GhcRn
11+
-> Maybe TcSigInfo
12+
-> TcM (LHsBinds GhcTc, TcGblEnv)
1513

1614
tcPatSynBuilderBind :: PatSynBind GhcRn GhcRn -> TcM (LHsBinds GhcTc)
1715

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
T15289.hs:5:16: error:
2-
• Couldn't match expected type ‘Maybe’ with actual type ‘Bool’
3-
• In the pattern: True
4-
In the pattern: True :: Maybe
5-
In the declaration for pattern synonym ‘What’
6-
|
7-
5 | pattern What = True :: Maybe
8-
| ^^^^
1+
2+
T15289.hs:5:16: error:
3+
• Couldn't match expected type ‘Maybe’ with actual type ‘Bool’
4+
• In the pattern: True
5+
In the pattern: True :: Maybe
6+
In the declaration for pattern synonym ‘What’
7+
8+
T15289.hs:5:24: error:
9+
• Expecting one more argument to ‘Maybe’
10+
Expected a type, but ‘Maybe’ has kind ‘* -> *’
11+
• In the type ‘Maybe’
12+
In a pattern type signature: Maybe
13+
In the pattern: True :: Maybe

testsuite/tests/patsyn/should_fail/all.T

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ test('T14380', normal, compile_fail, [''])
4242
test('T14498', normal, compile_fail, [''])
4343
test('T14552', normal, compile_fail, [''])
4444
test('T14507', normal, compile_fail, ['-dsuppress-uniques'])
45-
test('T15289', expect_broken(15289), compile_fail, [''])
45+
test('T15289', normal, compile_fail, [''])

0 commit comments

Comments
 (0)