Skip to content

Commit f02a022

Browse files
⅄ trunk → 25-11-05-difftool
2 parents 283defe + d1a2e70 commit f02a022

File tree

27 files changed

+848
-206
lines changed

27 files changed

+848
-206
lines changed

.github/workflows/bundle-ucm.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ jobs:
2424
os:
2525
- ubuntu-24.04
2626
- ubuntu-24.04-arm
27-
- macos-13 # intel
28-
- macos-14 # apple silicon
27+
- macos-15-intel
28+
- macos-latest
2929
- windows-2025
3030
# - windows-11-arm # 2025-06-17 apparently there's no stack or ghc builds for arm64 windows
3131
runs-on: ${{matrix.os}}
@@ -105,8 +105,8 @@ jobs:
105105
os:
106106
- ubuntu-24.04
107107
- ubuntu-24.04-arm
108-
- macos-13
109-
- macos-14
108+
- macos-15-intel
109+
- macos-latest
110110
- windows-2025
111111
# - windows-11-arm
112112
steps:

.github/workflows/ci.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ jobs:
3939
# While iterating on this file, you can disable one or more of these to speed things up
4040
- ubuntu-24.04
4141
- ubuntu-24.04-arm
42-
- macos-13
43-
- macos-14
42+
- macos-15-intel
43+
- macos-latest
4444
- windows-2025
4545
# - windows-11-arm
4646
steps:
@@ -210,8 +210,8 @@ jobs:
210210
# While iterating on this file, you can disable one or more of these to speed things up
211211
- ubuntu-24.04
212212
- ubuntu-24.04-arm
213-
- macos-13
214-
- macos-14
213+
- macos-15-intel
214+
- macos-latest
215215
- windows-2025
216216
# - windows-11-arm
217217
steps:
@@ -297,8 +297,8 @@ jobs:
297297
# While iterating on this file, you can disable one or more of these to speed things up
298298
- ubuntu-24.04
299299
- ubuntu-24.04-arm
300-
- macos-13
301-
- macos-14
300+
- macos-15-intel
301+
- macos-latest
302302
# - windows-2025 fails! :-\ https://github.com/unisonweb/unison/actions/runs/15706568824/job/44253742036?pr=5765
303303
# - windows-11-arm
304304
steps:

.github/workflows/nix-dev-cache.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ jobs:
2323
os:
2424
# - ubuntu-24.04 disabling this because it's been failing for a while
2525
# - ubuntu-24.04-arm https://github.com/unisonweb/unison/issues/5789
26-
- macos-13
27-
- macos-15
26+
- macos-15-intel
27+
- macos-latest
2828
steps:
2929
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
3030
- name: mount Nix store on larger partition

.github/workflows/tmate.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ on:
99
options:
1010
- ubuntu-24.04
1111
- ubuntu-24.04-arm
12-
- macos-13
13-
- macos-14
12+
- macos-15-intel
13+
- macos-latest
1414
- windows-2022
1515
- windows-2025
1616
- windows-11-arm

.github/workflows/update-transcripts.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
strategy:
1313
matrix:
1414
os:
15-
- macos-13
15+
- macos-15-intel
1616
steps:
1717
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
1818
- uses: unisonweb/actions/stack/cache/restore@main

parser-typechecker/src/Unison/PrintError.hs

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import Unison.Util.AnnotatedText qualified as AT
7575
import Unison.Util.ColorText (Color)
7676
import Unison.Util.ColorText qualified as Color
7777
import Unison.Util.Monoid (intercalateMap)
78+
import Unison.Util.Monoid qualified as Monoid
7879
import Unison.Util.Pretty (ColorText, Pretty)
7980
import Unison.Util.Pretty qualified as Pr
8081
import Unison.Util.Range (Range (..), startingLine)
@@ -332,14 +333,6 @@ renderTypeError e env src = case e of
332333
" expression ",
333334
"need to have the same type."
334335
]
335-
NotFunctionApplication {..} ->
336-
mconcat
337-
[ "This looks like a function call, but with a ",
338-
style Type1 (renderType' env ft),
339-
" where the function should be. Are you missing an operator?\n\n",
340-
annotatedAsStyle Type1 src f,
341-
debugSummary note
342-
]
343336
ActionRestrictionFailure {..} ->
344337
mconcat
345338
[ Pr.lines
@@ -361,6 +354,87 @@ renderTypeError e env src = case e of
361354
],
362355
debugSummary note
363356
]
357+
FunctionUnderApplied {..} ->
358+
let expectedTypeStr = style Type2 (renderType' env expectedLeaf)
359+
actualTypeStr = style ErrorSite (renderType' env foundLeaf)
360+
in mconcat
361+
[ "This call-site has type " <> actualTypeStr <> ":\n",
362+
showSourceMaybes src [styleAnnotated ErrorSite foundLeaf],
363+
"\n\n",
364+
"But I expected the type " <> expectedTypeStr <> " because of:\n",
365+
showSourceMaybes
366+
src
367+
[ (,Type1) . startingLine <$> (rangeForAnnotated mismatchSite),
368+
(,Type2) <$> rangeForAnnotated expectedLeaf
369+
],
370+
"\n\n",
371+
Pr.lines
372+
[ "It looks like the function application is missing these arguments:\n",
373+
Pr.indentN 2 $ Monoid.intercalateMap ", " (style Type2 . renderType' env) needArgs
374+
],
375+
unitHint,
376+
intLiteralSyntaxTip mismatchSite expectedType,
377+
debugNoteLoc
378+
. mconcat
379+
$ [ "\nloc debug:",
380+
"\n mismatchSite: ",
381+
annotatedToEnglish mismatchSite,
382+
"\n foundType: ",
383+
annotatedToEnglish foundType,
384+
"\n foundLeaf: ",
385+
annotatedToEnglish foundLeaf,
386+
"\n expectedType: ",
387+
annotatedToEnglish expectedType,
388+
"\n expectedLeaf: ",
389+
annotatedToEnglish expectedLeaf,
390+
"\n"
391+
],
392+
debugSummary note
393+
]
394+
where
395+
unitHintMsg =
396+
"\nHint: Actions within a block must have type "
397+
<> style Type2 (renderType' env expectedLeaf)
398+
<> ".\n"
399+
<> " Use "
400+
<> style Type1 "_ = <expr>"
401+
<> " to ignore a result."
402+
unitHint = if giveUnitHint then unitHintMsg else ""
403+
giveUnitHint = case expectedType of
404+
Type.Ref' u | u == unitRef -> case mismatchSite of
405+
Term.Let1Named' v _ _ -> Var.isAction v
406+
_ -> False
407+
_ -> False
408+
NotFunctionApplication {..} ->
409+
case Type.arityIgnoringEffects ft of
410+
0 ->
411+
mconcat
412+
[ "It looks like" <> style ErrorSite " this " <> "expression is being called like a function:\n\n",
413+
annotatedAsStyle ErrorSite src f,
414+
"\n\nbut the thing being applied has the type:\n\n",
415+
style Type2 (renderType' env ft),
416+
"\n\nWhich doesn't expect any arguments.",
417+
"\n\n",
418+
debugSummary note
419+
]
420+
arity ->
421+
mconcat
422+
[ "It looks like" <> style ErrorSite " this " <> "function call:\n\n",
423+
annotatedAsStyle ErrorSite src f,
424+
"\n\nis being applied to ",
425+
Pr.blue $ Pr.shown (length args),
426+
" arguments, but it has the type\n\n",
427+
Pr.indentN 2 $ style Type2 (renderType' env ft),
428+
"\n\nwhich only accepts ",
429+
Pr.blue $ Pr.shown arity,
430+
maybePlural " argument" arity <> ".\n\n",
431+
"Maybe you applied the function to too many arguments?\n\n",
432+
debugSummary note
433+
]
434+
where
435+
maybePlural word n
436+
| n == 1 = word
437+
| otherwise = word <> "s"
364438
FunctionApplication {..} ->
365439
let fte = Type.removePureEffects False ft
366440
fteFreeVars = Set.map TypeVar.underlying $ ABT.freeVars fte
@@ -454,18 +528,14 @@ renderTypeError e env src = case e of
454528
"\n\n",
455529
showSourceMaybes
456530
src
457-
[ -- these are overwriting the colored ranges for some reason?
458-
-- (,Color.ForceShow) <$> rangeForAnnotated mismatchSite
459-
-- , (,Color.ForceShow) <$> rangeForType foundType
460-
-- , (,Color.ForceShow) <$> rangeForType expectedType
461-
-- ,
462-
(,Type1) . startingLine <$> (rangeForAnnotated mismatchSite),
531+
[ (,Type1) . startingLine <$> (rangeForAnnotated mismatchSite),
463532
(,Type2) <$> rangeForAnnotated expectedLeaf
464533
],
465534
fromOverHere'
466535
src
467536
[styleAnnotated Type1 foundLeaf]
468537
[styleAnnotated Type2 expectedLeaf],
538+
missingDelayHint,
469539
unitHint,
470540
intLiteralSyntaxTip mismatchSite expectedType,
471541
debugNoteLoc
@@ -486,6 +556,20 @@ renderTypeError e env src = case e of
486556
debugSummary note
487557
]
488558
where
559+
missingDelayHint = case additionalInfo of
560+
Nothing -> ""
561+
Just MissingDelay ->
562+
Pr.lines
563+
[ "I expected the expression to be delayed, but it was not.",
564+
"Are you missing a `do`?"
565+
]
566+
Just SuperfluousDelay ->
567+
Pr.lines
568+
[ "",
569+
"I didn't expect this expression to be delayed, but it was.",
570+
"Are you using a `do` where you don't need one,",
571+
"or are you missing a `()` to force an expression?"
572+
]
489573
unitHintMsg =
490574
"\nHint: Actions within a block must have type "
491575
<> style Type2 (renderType' env expectedLeaf)

parser-typechecker/src/Unison/Typechecker.hs

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module Unison.Typechecker
1010
isEqual,
1111
isSubtype,
1212
fitsScheme,
13+
isMismatchMissingDelay,
1314
Env (..),
1415
Notes (..),
1516
Resolution (..),
@@ -32,6 +33,7 @@ import Data.Text qualified as Text
3233
import Data.Tuple qualified as Tuple
3334
import Unison.ABT qualified as ABT
3435
import Unison.Blank qualified as B
36+
import Unison.Builtin.Decls qualified as BuiltinDecls
3537
import Unison.Codebase.BuiltinAnnotation (BuiltinAnnotation)
3638
import Unison.Name qualified as Name
3739
import Unison.Prelude
@@ -43,6 +45,7 @@ import Unison.Syntax.Name qualified as Name (unsafeParseText, unsafeParseVar)
4345
import Unison.Term (Term)
4446
import Unison.Term qualified as Term
4547
import Unison.Type (Type)
48+
import Unison.Type qualified as Type
4649
import Unison.Typechecker.Context qualified as Context
4750
import Unison.Typechecker.TypeLookup qualified as TL
4851
import Unison.Typechecker.TypeVar qualified as TypeVar
@@ -126,6 +129,33 @@ synthesize ppe pmccSwitch env t =
126129
(TypeVar.liftTerm t)
127130
in Result.hoist (pure . runIdentity) $ fmap TypeVar.lowerType result
128131

132+
-- | @subtype a b@ is @Right b@ iff @f x@ is well-typed given
133+
-- @x : a@ and @f : b -> t@. That is, if a value of type `a`
134+
-- can be passed to a function expecting a `b`, then `subtype a b`
135+
-- returns `Right b`. This function returns @Left note@ with information
136+
-- about the reason for subtyping failure otherwise.
137+
--
138+
-- Example: @subtype (forall a. a -> a) (Int -> Int)@ returns @Right (Int -> Int)@.
139+
-- subtype :: Var v => Type v -> Type v -> Either Note (Type v)
140+
-- subtype t1 t2 = error "todo"
141+
-- let (t1', t2') = (ABT.vmap TypeVar.Universal t1, ABT.vmap TypeVar.Universal t2)
142+
-- in case Context.runM (Context.subtype t1' t2')
143+
-- (Context.MEnv Context.env0 [] Map.empty True) of
144+
-- Left e -> Left e
145+
-- Right _ -> Right t2
146+
147+
-- | Returns true if @subtype t1 t2@ returns @Right@, false otherwise
148+
-- isSubtype :: Var v => Type v -> Type v -> Bool
149+
-- isSubtype t1 t2 = case subtype t1 t2 of
150+
-- Left _ -> False
151+
-- Right _ -> True
152+
153+
-- | Returns true if the two type are equal, up to alpha equivalence and
154+
-- order of quantifier introduction. Note that alpha equivalence considers:
155+
-- `forall b a . a -> b -> a` and
156+
-- `forall a b . a -> b -> a` to be different types
157+
-- equals :: Var v => Type v -> Type v -> Bool
158+
-- equals t1 t2 = isSubtype t1 t2 && isSubtype t2 t1
129159
isSubtype :: (Var v) => Type v loc -> Type v loc -> Bool
130160
isSubtype t1 t2 =
131161
handleCompilerBug (Context.isSubtype (tvar $ void t1) (tvar $ void t2))
@@ -450,30 +480,12 @@ wellTyped ppe env term =
450480
enable =
451481
Context.PatternMatchCoverageCheckAndKindInferenceSwitch'Enabled
452482

453-
-- | @subtype a b@ is @Right b@ iff @f x@ is well-typed given
454-
-- @x : a@ and @f : b -> t@. That is, if a value of type `a`
455-
-- can be passed to a function expecting a `b`, then `subtype a b`
456-
-- returns `Right b`. This function returns @Left note@ with information
457-
-- about the reason for subtyping failure otherwise.
458-
--
459-
-- Example: @subtype (forall a. a -> a) (Int -> Int)@ returns @Right (Int -> Int)@.
460-
-- subtype :: Var v => Type v -> Type v -> Either Note (Type v)
461-
-- subtype t1 t2 = error "todo"
462-
-- let (t1', t2') = (ABT.vmap TypeVar.Universal t1, ABT.vmap TypeVar.Universal t2)
463-
-- in case Context.runM (Context.subtype t1' t2')
464-
-- (Context.MEnv Context.env0 [] Map.empty True) of
465-
-- Left e -> Left e
466-
-- Right _ -> Right t2
467-
468-
-- | Returns true if @subtype t1 t2@ returns @Right@, false otherwise
469-
-- isSubtype :: Var v => Type v -> Type v -> Bool
470-
-- isSubtype t1 t2 = case subtype t1 t2 of
471-
-- Left _ -> False
472-
-- Right _ -> True
473-
474-
-- | Returns true if the two type are equal, up to alpha equivalence and
475-
-- order of quantifier introduction. Note that alpha equivalence considers:
476-
-- `forall b a . a -> b -> a` and
477-
-- `forall a b . a -> b -> a` to be different types
478-
-- equals :: Var v => Type v -> Type v -> Bool
479-
-- equals t1 t2 = isSubtype t1 t2 && isSubtype t2 t1
483+
-- | Checks if the mismatch between two types is due to a missing delay, if so returns a tag for which type is
484+
-- missing the delay
485+
isMismatchMissingDelay :: (Var v) => Type v loc -> Type v loc -> Maybe (Either (Type v loc) (Type v loc))
486+
isMismatchMissingDelay typeA typeB
487+
| isSubtype (Type.arrow () (Type.ref () BuiltinDecls.unitRef) (typeA $> ())) (typeB $> ()) =
488+
Just (Left typeA)
489+
| isSubtype (ABT.tm (ABT.tm (Type.Ref BuiltinDecls.unitRef) `Type.Arrow` (typeB $> ()))) (typeA $> ()) =
490+
Just (Right typeB)
491+
| otherwise = Nothing

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

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2638,13 +2638,16 @@ checkWanted exact want (Term.Lam' boundVarAnn body) (Type.Arrow'' i es o) = do
26382638
checkWanted exact want tm@(Term.Var' _) ty@(Type.Arrow'' i es o) =
26392639
synthesize tm >>= \case
26402640
-- special case to detect quadratic abilities
2641-
(Type.Arrow'' j fs p, wnew) -> do
2641+
(ts@(Type.Arrow'' j fs p), wnew) -> do
26422642
ctx <- getContext
2643-
subtype (apply ctx i) (apply ctx j)
2644-
ctx <- getContext
2645-
sub <- subAbilities ((Nothing,) . apply ctx <$> fs) (apply ctx <$> es)
2646-
ctx <- getContext
2647-
subtype (apply ctx p) (apply ctx o)
2643+
sub <- scope (InSubtype ts ty) do
2644+
-- morally this is a subtype call, but we've expanded it to
2645+
-- get some better information.
2646+
subtype (apply ctx i) (apply ctx j)
2647+
ctx <- getContext
2648+
sub <- subAbilities ((Nothing,) . apply ctx <$> fs) (apply ctx <$> es)
2649+
ctx <- getContext
2650+
sub <$ subtype (apply ctx p) (apply ctx o)
26482651
exactAbilitiesWarning fs es exact sub
26492652
coalesceWanted wnew want
26502653
(u, wnew) -> do
@@ -2681,6 +2684,31 @@ checkWanted _ want e@(Term.Match' scrut cases) t = do
26812684
PatternMatchCoverageCheckAndKindInferenceSwitch'Disabled ->
26822685
pure ()
26832686
pure want
2687+
checkWanted exact want (Term.If' cond t f) ty = do
2688+
want <-
2689+
scope InIfCond
2690+
. checkWanted exact want cond
2691+
. Type.boolean
2692+
$ loc cond
2693+
ty <- applyM ty
2694+
want <- scope (InIfBody $ loc t) $ checkWantedScoped bexact want t ty
2695+
ty <- applyM ty
2696+
scope (InIfBody $ loc f) $ checkWantedScoped bexact want f ty
2697+
where
2698+
bexact = isJust exact
2699+
checkWanted exact want (Term.List' es) lty
2700+
| Type.App' (Type.Ref' r) te <- lty,
2701+
r == Type.listRef =
2702+
let f want e = checkWantedScoped bexact want e =<< applyM te
2703+
in Foldable.foldlM f want es
2704+
| Type.Var' (TypeVar.Existential _ v) <- lty = do
2705+
ev <- extendExistential v
2706+
let te = existentialp (loc lty) ev
2707+
subtype (Type.app' (Type.ref (loc lty) Type.listRef) te) lty
2708+
let f want e = checkWantedScoped bexact want e =<< applyM te
2709+
Foldable.foldlM f want es
2710+
where
2711+
bexact = isJust exact
26842712
checkWanted _ want e t = do
26852713
(u, wnew) <- synthesize e
26862714
ctx <- getContext

0 commit comments

Comments
 (0)