Skip to content

Commit 4d04ae4

Browse files
authored
Merge pull request #5837 from unisonweb/topic/type-errors
Attempt a couple type error improvements
2 parents 63477ee + 8fb7e1f commit 4d04ae4

File tree

7 files changed

+83
-39
lines changed

7 files changed

+83
-39
lines changed

parser-typechecker/src/Unison/PrintError.hs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,26 @@ renderTypeError e env src = case e of
339339
annotatedAsStyle Type1 src f,
340340
debugSummary note
341341
]
342+
ActionRestrictionFailure {..} ->
343+
mconcat
344+
[ Pr.lines
345+
[ "I found an action in a block with a type of:",
346+
"",
347+
Pr.indentN 4 . style Type1 $ renderType' env foundType,
348+
"",
349+
showSourceMaybes src
350+
[(,Type1) <$> rangeForAnnotated mismatchSite],
351+
Pr.wrap . mconcat $
352+
[ "All actions are expected to have a type of",
353+
style Type2 "Unit",
354+
"to catch accidental delayed values. To explicitly",
355+
"ignore a result, use:"
356+
],
357+
"",
358+
Pr.indentN 4 $ style Type1 "_ = <expr>"
359+
],
360+
debugSummary note
361+
]
342362
FunctionApplication {..} ->
343363
let fte = Type.removePureEffects False ft
344364
fteFreeVars = Set.map TypeVar.underlying $ ABT.freeVars fte

parser-typechecker/src/Unison/Typechecker/Context.hs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ data PathElement v loc
334334
| InMatch loc -- location of 1st case body
335335
| InMatchGuard
336336
| InMatchBody
337+
| InActionRestriction
337338
deriving (Show)
338339

339340
type ExpectedArgCount = Int
@@ -1275,7 +1276,7 @@ synthesizeWanted tm@(Term.Request' r) =
12751276
synthesizeWanted (Term.Let1Top' top binding boundVarAnn e) = do
12761277
(tbinding, wb) <- synthesizeBinding top binding
12771278
v' <- ABT.freshen e freshenVar
1278-
when (Var.isAction (ABT.variable e)) $
1279+
when (Var.isAction (ABT.variable e)) . scope InActionRestriction $
12791280
-- enforce that actions in a block have type ()
12801281
subtype tbinding (DDB.unitType (ABT.annotation binding))
12811282
appendContext [Ann v' boundVarAnn tbinding]
@@ -1428,7 +1429,9 @@ synthesizeWanted e
14281429
outputTypev <- freshenVar (Var.named "match-output")
14291430
let outputType = existential' l B.Blank outputTypev
14301431
appendContext [existential outputTypev]
1431-
cwant <- checkCases scrutineeType outputType cases
1432+
cwant <-
1433+
scope (InMatch (ABT.annotation outputType)) $
1434+
checkCases scrutineeType outputType cases
14321435
want <- coalesceWanted cwant swant
14331436
ctx <- getContext
14341437
let matchType = apply ctx outputType
@@ -1625,13 +1628,12 @@ checkCases ::
16251628
[Term.MatchCase loc (Term v loc)] ->
16261629
M v loc (Wanted v loc)
16271630
checkCases _ _ [] = pure []
1628-
checkCases scrutType outType cases@(Term.MatchCase _ _ t : _) =
1629-
scope (InMatch (ABT.annotation t)) $ do
1630-
mes <- requestType (cases <&> \(Term.MatchCase p _ _) -> p)
1631-
for_ mes $ \es ->
1632-
applyM scrutType >>= \sty -> ensureReqEffects sty es
1633-
scrutType' <- applyM =<< ungeneralize scrutType
1634-
coalesceWanteds =<< traverse (checkCase scrutType' outType) cases
1631+
checkCases scrutType outType cases = do
1632+
mes <- requestType (cases <&> \(Term.MatchCase p _ _) -> p)
1633+
for_ mes $ \es ->
1634+
applyM scrutType >>= \sty -> ensureReqEffects sty es
1635+
scrutType' <- applyM =<< ungeneralize scrutType
1636+
coalesceWanteds =<< traverse (checkCase scrutType' outType) cases
16351637

16361638
-- Checks a scrutinee type against a list of effects from e.g. a list of cases
16371639
-- from a handler.
@@ -1970,8 +1972,10 @@ annotateLetRecBindings isTop letrec =
19701972
Foldable.for_ (zip3 vs bindings bindingTypes) $ \(v, b, t) -> do
19711973
-- note: elements of a cycle have to be pure, otherwise order of effects
19721974
-- is unclear and chaos ensues
1975+
19731976
-- ensure actions in blocks have type ()
1974-
when (Var.isAction v) $ subtype t (DDB.unitType (ABT.annotation b))
1977+
when (Var.isAction v) . scope InActionRestriction $
1978+
subtype t (DDB.unitType (ABT.annotation b))
19751979
insideDef v $ checkScopedWith b t []
19761980
ensureGuardedCycle (vs `zip` bindings)
19771981
pure (bindings, bindingTypes, vlocs)
@@ -2569,7 +2573,7 @@ checkWanted exact want (Term.Let1Top' top binding boundVarAnn m) t = do
25692573
want <- coalesceWanted wbinding want
25702574
v <- ABT.freshen m freshenVar
25712575
markThenRetractWanted v $ do
2572-
when (Var.isAction (ABT.variable m)) $
2576+
when (Var.isAction (ABT.variable m)) . scope InActionRestriction $
25732577
-- enforce that actions in a block have type ()
25742578
subtype tbinding (DDB.unitType (ABT.annotation binding))
25752579
extendContext (Ann v boundVarAnn tbinding)

parser-typechecker/src/Unison/Typechecker/Extractor.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ inCheck = asPathExtractor $ \case
189189
-- inInstantiateL
190190
-- inInstantiateR
191191

192+
inActionRestriction :: SubseqExtractor v loc ()
193+
inActionRestriction = asPathExtractor $ \case
194+
C.InActionRestriction -> Just ()
195+
_ -> Nothing
196+
192197
inSynthesizeApp :: SubseqExtractor v loc (C.Type v loc, C.Term v loc, Int)
193198
inSynthesizeApp = asPathExtractor $ \case
194199
C.InSynthesizeApp t e n -> Just (t, e, n)

parser-typechecker/src/Unison/Typechecker/TypeError.hs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ data TypeError v loc
4444
mismatchSite :: C.Term v loc,
4545
note :: C.ErrorNote v loc
4646
}
47+
| ActionRestrictionFailure
48+
{ foundType :: C.Type v loc,
49+
mismatchSite :: C.Term v loc,
50+
note :: C.ErrorNote v loc
51+
}
4752
| FunctionApplication
4853
{ f :: C.Term v loc,
4954
ft :: C.Type v loc,
@@ -143,6 +148,7 @@ allErrors =
143148
[ and,
144149
or,
145150
cond,
151+
actionRestriction,
146152
matchGuard,
147153
ifBody,
148154
listBody,
@@ -347,6 +353,19 @@ existentialMismatch0 em getExpectedLoc = do
347353
-- todo : save type leaves too
348354
n
349355

356+
actionRestriction ::
357+
(Var v, Ord loc) =>
358+
Ex.ErrorExtractor v loc (TypeError v loc)
359+
actionRestriction = do
360+
Ex.unique Ex.inActionRestriction
361+
note <- Ex.errorNote
362+
mismatchSite <- Ex.innermostTerm
363+
path <- Ex.path
364+
let subtypes = [ t1 | C.InSubtype t1 _ <- path ]
365+
guard . not $ null subtypes
366+
let foundType = Type.cleanup $ last subtypes
367+
pure $ ActionRestrictionFailure foundType mismatchSite note
368+
350369
ifBody,
351370
listBody,
352371
matchBody ::

unison-cli/src/Unison/LSP/FileAnalysis.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ analyseNotes fileUri ppe src notes = do
286286
TypeError.NotFunctionApplication {f} -> singleRange $ ABT.annotation f
287287
TypeError.AbilityCheckFailure {abilityCheckFailureSite} -> singleRange abilityCheckFailureSite
288288
TypeError.AbilityEqFailure {abilityCheckFailureSite} -> singleRange abilityCheckFailureSite
289+
TypeError.ActionRestrictionFailure {mismatchSite} ->
290+
singleRange $ ABT.annotation mismatchSite
289291
TypeError.AbilityEqFailureFromAp {expectedSite, mismatchSite} -> do
290292
let locs = [ABT.annotation expectedSite, ABT.annotation mismatchSite]
291293
(r, rs) <- withNeighbours (locs >>= aToR)

unison-src/transcripts/idempotent/fix614.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,17 @@ ex2 = do
4040
``` ucm :added-by-ucm
4141
Loading changes detected in scratch.u.
4242
43-
I found a value of type: a1 ->{Stream a1} Unit
44-
where I expected to find: Unit
43+
I found an action in a block with a type of:
44+
45+
a ->{Stream a} Unit
4546
4647
2 | Stream.emit
4748
3 | 42
4849
49-
Hint: Actions within a block must have type Unit.
50-
Use _ = <expr> to ignore a result.
50+
All actions are expected to have a type of Unit to catch
51+
accidental delayed values. To explicitly ignore a result, use:
52+
53+
_ = <expr>
5154
```
5255

5356
We can explicitly ignore an unused result like so:
@@ -96,16 +99,15 @@ ex4 =
9699
``` ucm :added-by-ucm
97100
Loading changes detected in scratch.u.
98101
99-
I found a value of type: [Nat]
100-
where I expected to find: Unit
102+
I found an action in a block with a type of:
103+
104+
[Nat]
101105
102106
2 | [1,2,3] -- no good
103107
3 | ()
104108
105-
from right here:
106-
107-
2 | [1,2,3] -- no good
109+
All actions are expected to have a type of Unit to catch
110+
accidental delayed values. To explicitly ignore a result, use:
108111
109-
Hint: Actions within a block must have type Unit.
110-
Use _ = <expr> to ignore a result.
112+
_ = <expr>
111113
```

unison-src/transcripts/idempotent/fix693.md

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,12 @@ h0 req = match req with
4343
``` ucm :added-by-ucm
4444
Loading changes detected in scratch.u.
4545
46-
Each case of a match / with expression need to have the same
47-
type.
48-
49-
Here, one is: Optional b
50-
and another is: Optional a
51-
52-
53-
3 | { X.x _ c -> _ } -> handle c with h0
54-
55-
from these spots, respectively:
46+
I found a value of type: a
47+
where I expected to find: b
5648
5749
1 | h0 : Request {X t} b -> Optional b
50+
2 | h0 req = match req with
51+
3 | { X.x _ c -> _ } -> handle c with h0
5852
```
5953

6054
This code should not check because `t` does not match `b`.
@@ -69,16 +63,14 @@ h1 req = match req with
6963
``` ucm :added-by-ucm
7064
Loading changes detected in scratch.u.
7165
72-
Each case of a match / with expression need to have the same
73-
type.
74-
75-
Here, one is: Optional b
76-
and another is: Optional t
77-
66+
I found a value of type: t
67+
where I expected to find: b
7868
69+
1 | h1 : Request {X t} b -> Optional b
70+
2 | h1 req = match req with
7971
3 | { X.x t _ -> _ } -> handle t with h1
8072
81-
from these spots, respectively:
73+
from right here:
8274
8375
1 | h1 : Request {X t} b -> Optional b
8476
```

0 commit comments

Comments
 (0)