Merge pull request #6221 from unisonweb/fix/6207 · JavaDerek/unison@6f59379 · GitHub
Skip to content

Commit 6f59379

Browse files
authored
Merge pull request unisonweb#6221 from unisonweb/fix/6207
Improve type checking of recursive lets with a mix of binding annotations
2 parents 3448cec + 8d47ab1 commit 6f59379

6 files changed

Lines changed: 233 additions & 71 deletions

File tree

Lines changed: 1 addition & 1 deletion

‎.github/workflows/proofs/tests.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
a5f459f7c3bb4ca79684ff1c710e44704c03fb9c09e1ad01e5ef90e04086a04f 7a39b8df88ef160e852c6aac30ee2a8bfdb8074b060d5afc8c72bf5f03d1f0a7 pass
1+
37ec63df5a1906ffa4520b1c2993a228a8a9555e0415b81198933281b74b6a79 7a39b8df88ef160e852c6aac30ee2a8bfdb8074b060d5afc8c72bf5f03d1f0a7 pass
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8b97c8236ca3229efc479a562ad93b802bbaa8a34fdc2f30efeb5bcf9b53bae6 72c0b9bfd651515ecb3d3f4981a6bdb8c6d52eba2308cd11fea004d31941aeef pass
1+
11b365a07b04255c18a3acabab1bc76533a6daadff43fe6711c7fc4966a1252a 72c0b9bfd651515ecb3d3f4981a6bdb8c6d52eba2308cd11fea004d31941aeef pass

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

Lines changed: 196 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1883,28 +1883,30 @@ resetContextAfter x a = do
18831883
setContext ctx
18841884
pure a
18851885

1886+
type LetRecInfo v loc =
1887+
(v -> M v loc v) -> M v loc ([((loc, v), Term v loc)], Term v loc)
1888+
18861889
-- | Synthesize and generalize the type of each binding in a let rec.
18871890
-- Updates the context so that all bindings are annotated with
18881891
-- their type. Also returns the freshened version of `body`.
18891892
-- See usage in `synthesize` and `check` for `LetRec'` case.
18901893
annotateLetRecBindings ::
18911894
(Var v, Ord loc, Semigroup loc) =>
1892-
Term.IsTop ->
1893-
((v -> M v loc v) -> M v loc ([((loc, v), Term v loc)], Term v loc)) ->
1894-
M v loc (Term v loc)
1895+
Term.IsTop -> LetRecInfo v loc -> M v loc (Term v loc)
18951896
annotateLetRecBindings isTop letrec =
18961897
-- If this is a top-level letrec, then emit a TopLevelComponent note,
18971898
-- which asks if the user-provided type annotations were needed.
18981899
if isTop
18991900
then do
19001901
-- First, typecheck (using annotateLetRecBindings') the bindings with any
19011902
-- user-provided annotations.
1902-
(body, vts) <- annotateLetRecBindings' True
1903+
(body, vts) <- annotateLetRecBindings' letrec True
19031904
-- Then, try typechecking again, but ignoring any user-provided annotations.
19041905
-- This will infer whatever type. If it altogether fails to typecheck here
19051906
-- then, ...(1)
19061907
withoutAnnotations <-
1907-
resetContextAfter Nothing $ Just <$> annotateLetRecBindings' False
1908+
resetContextAfter Nothing $
1909+
Just <$> annotateLetRecBindings' letrec False
19081910
-- convert from typechecker TypeVar back to regular `v` vars
19091911
let unTypeVar (v, t) = (v, generalizeAndUnTypeVar t)
19101912
case withoutAnnotations of
@@ -1918,58 +1920,147 @@ annotateLetRecBindings isTop letrec =
19181920
pure body
19191921
else do
19201922
-- If this isn't a top-level letrec, then we don't have to do anything special
1921-
(body, _vts) <- annotateLetRecBindings' True
1923+
(body, _vts) <- annotateLetRecBindings' letrec True
19221924
pure body
1925+
1926+
-- A wrapper type for demuxed binding information in a let. Only used
1927+
-- locally.
1928+
data Bindings v loc
1929+
= Bindings
1930+
{ bnds :: [Term v loc], -- actual bindings
1931+
bndTyps :: [Type v loc], -- types for the bindings
1932+
bndVars :: [v], -- variables the bindings are bound to
1933+
bndVarLocs :: [loc] -- locations of said variables
1934+
}
1935+
1936+
demuxBindings :: [(Term v loc, Type v loc, v, loc)] -> Bindings v loc
1937+
demuxBindings = uncurry4 Bindings . unzip4
1938+
1939+
-- Calculates the associations between variables and their bindings in
1940+
-- a `Bindings`, discarding some of the other information.
1941+
namedBindings :: Bindings v loc -> [(v, Term v loc)]
1942+
namedBindings bs = bndVars bs `zip` bnds bs
1943+
1944+
-- Helper function for checking recursive let bindings. This is the
1945+
-- part of the check that processes the type of the _bindings_, and
1946+
-- returns information that can be used to subsequently check the
1947+
-- body.
1948+
--
1949+
-- To achieve better results, this classifies the bindings into two
1950+
-- categories
1951+
--
1952+
-- 1. Bindings given with completely closed types, that need no
1953+
-- parts inferred.
1954+
-- 2. Bindings that need at least some part of the type inferred.
1955+
--
1956+
-- The first category can be checked _last_, under the presumption
1957+
-- that the given types are accurate, because the checking of the
1958+
-- second category has no effect on the types in the first category.
1959+
-- This allows us to _generalize_ the types in the second category
1960+
-- before checking the first category. This is necessary when you have
1961+
-- examples like:
1962+
--
1963+
-- f x = g x
1964+
-- g : a -> b
1965+
-- g x = f x
1966+
--
1967+
-- because otherwise the inferred type of `f` will seem too concrete
1968+
-- for the checking of `g` to succeed.
1969+
--
1970+
-- Note however that "closed" can be somewhat deceptive. The type of
1971+
-- `g` above is actually not closed, because it is shorthand for the
1972+
-- type `a ->{e} b`, where the `e` needs to be inferred. To avoid this
1973+
-- defeating the whole approach, it's advisable to be somewhat
1974+
-- conservative with making up these inference variables. For instance
1975+
--
1976+
-- f : a -> b -> c ->{E} d
1977+
-- f x y z = ...
1978+
--
1979+
-- can be immediately completed to `a ->{} b ->{} c ->{E} d` instead
1980+
-- of making up variables for the two positions that must be pure.
1981+
annotateLetRecBindings' ::
1982+
(Var v, Ord loc, Semigroup loc) =>
1983+
LetRecInfo v loc ->
1984+
Bool ->
1985+
M v loc (Term v loc, [(v, Type v loc)])
1986+
annotateLetRecBindings' letrec useAnn = do
1987+
(binds, body) <- letrec freshenVar
1988+
1989+
((allbnds, abnds, ubnds), ctx) <-
1990+
markThenRetract Var.inferOther $ do
1991+
binds <- traverse prepare binds
1992+
let (unanns, anns) = partitionEithers binds
1993+
ubnds = demuxBindings unanns
1994+
abnds = demuxBindings anns
1995+
allbnds = demuxBindings $ map (either id id) binds
1996+
1997+
-- check un-annotated bindings against their types
1998+
appendContext (varAnns allbnds)
1999+
2000+
Foldable.for_ (vbts ubnds) \(v, b, t) -> do
2001+
-- elements of a cycle need to be pure, otherwise order of
2002+
-- effects is unclear and chaos ensues.
2003+
2004+
-- ensure actions in blocks have type ()
2005+
when (Var.isAction v) . scope InActionRestriction $
2006+
subtype t (DDB.unitType (ABT.annotation b))
2007+
insideDef v $ checkScopedWith b t []
2008+
2009+
pure (allbnds, abnds, ubnds)
2010+
2011+
-- compute generalized types for bindings that needed inference
2012+
vars <- getVariances
2013+
let bindingArities = Term.arity <$> bnds ubnds
2014+
gen bty _arity =
2015+
generalizeExistentials ctx vars bty
2016+
gbndTyps = zipWith gen (bndTyps ubnds) bindingArities
2017+
genAnns =
2018+
zipWith3 Ann (bndVars ubnds) (bndVarLocs ubnds) gbndTyps
2019+
2020+
allAnns = genAnns ++ varAnns abnds
2021+
2022+
appendContext allAnns
2023+
2024+
-- check annotated bindings
2025+
_ <- markThenRetract Var.inferOther $ do
2026+
Foldable.for_ (vbts abnds) \(v, b, t) -> do
2027+
when (Var.isAction v) . scope InActionRestriction $
2028+
subtype t (DDB.unitType (ABT.annotation b))
2029+
insideDef v $ checkScopedWith b t []
2030+
2031+
ensureGuardedCycle (namedBindings allbnds)
2032+
2033+
let vTypes =
2034+
zip (bndVars ubnds) gbndTyps
2035+
++ zip (bndVars abnds) (bndTyps abnds)
2036+
2037+
pure (body, vTypes)
19232038
where
1924-
annotateLetRecBindings' useUserAnnotations = do
1925-
(bindings, body) <- letrec freshenVar
1926-
let vs = map (snd . fst) bindings
1927-
((bindings, bindingTypes, vlocs), ctx2) <- markThenRetract Var.inferOther $ do
1928-
let f ((vloc, v), binding) = case binding of
1929-
-- If user has provided an annotation, we use that
1930-
Term.Ann' e t | useUserAnnotations -> do
1931-
-- Arrows in `t` with no ability lists get an attached fresh
1932-
-- existential to allow inference of required abilities
1933-
t2 <- existentializeArrows =<< applyM t
1934-
pure (Term.ann (loc binding) e t2, t2, vloc)
1935-
-- If we're not using an annotation, we make one up. There's 2 cases:
1936-
1937-
lam@(Term.Lam' {}) ->
1938-
-- If `e` is a lambda of arity K, we immediately refine the
1939-
-- existential to `a1 ->{e1} a2 ... ->{eK} r`. This gives better
1940-
-- inference of the lambda's ability variables in conjunction with
1941-
-- handling of lambdas in `check` judgement.
1942-
(lam,,vloc) <$> existentialFunctionTypeFor lam
1943-
e -> do
1944-
-- Anything else, just make up a fresh existential
1945-
-- which will be refined during typechecking of the binding
1946-
vt <- extendExistential v
1947-
pure $ (e, existential' (loc binding) B.Blank vt, vloc)
1948-
(bindings, bindingTypes, vlocs) <- unzip3 <$> traverse f bindings
1949-
appendContext (zipWith3 Ann vs vlocs bindingTypes)
1950-
-- check each `bi` against its type
1951-
Foldable.for_ (zip3 vs bindings bindingTypes) $ \(v, b, t) -> do
1952-
-- note: elements of a cycle have to be pure, otherwise order of effects
1953-
-- is unclear and chaos ensues
1954-
1955-
-- ensure actions in blocks have type ()
1956-
when (Var.isAction v) . scope InActionRestriction $
1957-
subtype t (DDB.unitType (ABT.annotation b))
1958-
insideDef v $ checkScopedWith b t []
1959-
ensureGuardedCycle (vs `zip` bindings)
1960-
pure (bindings, bindingTypes, vlocs)
1961-
-- compute generalized types `gt1, gt2 ...` for each binding `b1, b2...`;
1962-
-- add annotations `v1 : gt1, v2 : gt2 ...` to the context
1963-
vars <- getVariances
1964-
let bindingArities = Term.arity <$> bindings
1965-
gen bindingType _arity =
1966-
generalizeExistentials ctx2 vars bindingType
1967-
bindingTypesGeneralized = zipWith gen bindingTypes bindingArities
1968-
annotations = zipWith3 Ann vs vlocs bindingTypesGeneralized
1969-
1970-
appendContext annotations
1971-
let vTypes = vs `zip` bindingTypesGeneralized
1972-
pure (body, vTypes)
2039+
closed = Set.null . ABT.freeVars
2040+
indicate t = if closed t then Right else Left
2041+
2042+
varAnns bs = zipWith3 Ann (bndVars bs) (bndVarLocs bs) (bndTyps bs)
2043+
2044+
vbts bs = zip3 (bndVars bs) (bnds bs) (bndTyps bs)
2045+
2046+
prepare ((vloc, v), binding)
2047+
-- If a term has an annotation add any missing ability
2048+
-- annotations. We indicate whether the type is completely
2049+
-- closed, because such types can be separated out of the
2050+
-- binding cycle.
2051+
| useAnn,
2052+
Term.Ann' e t <- binding = do
2053+
t <- addAbilities (countLambdas e) =<< applyM t
2054+
pure $ indicate t (Term.ann (loc binding) e t, t, v, vloc)
2055+
-- If the term is a lambda, we immediately make a refined type
2056+
-- for better inference.
2057+
| Term.Lam' {} <- binding =
2058+
Left . (binding,,v,vloc)
2059+
<$> existentialFunctionTypeFor binding
2060+
-- otherwise just make up a fresh existential and proceed
2061+
| otherwise = do
2062+
vt <- extendExistential v
2063+
pure $ Left (binding, existential' (loc binding) B.Blank vt, v, vloc)
19732064

19742065
ensureGuardedCycle :: (Var v) => [(v, Term v loc)] -> M v loc ()
19752066
ensureGuardedCycle bindings =
@@ -1984,16 +2075,24 @@ ensureGuardedCycle bindings =
19842075
then pure ()
19852076
else failWith $ UnguardedLetRecCycle (fst <$> notok) bindings
19862077

1987-
existentialFunctionTypeFor :: (Ord loc, Var v) => Term v loc -> M v loc (Type v loc)
1988-
existentialFunctionTypeFor lam@(Term.LamNamed' v body) = do
1989-
v <- extendExistential v
2078+
-- Create a n existential type for a lambda term.
2079+
--
2080+
-- Note: for lambdas like `x y z -> ...` only the abilities after z
2081+
-- will ever be non-empty, so we can just make up empty ability lists
2082+
-- for repeated lambdas.
2083+
existentialFunctionTypeFor ::
2084+
(Ord loc, Var v) => Term v loc -> M v loc (Type v loc)
2085+
existentialFunctionTypeFor lam@(Term.LamsNamed' vs _) = do
2086+
let expl = existentialp (loc lam)
2087+
us <- traverse extendExistential vs
19902088
e <- extendExistential Var.inferAbility
1991-
o <- existentialFunctionTypeFor body
1992-
pure $
1993-
Type.arrow
1994-
(loc lam)
1995-
(existentialp (loc lam) v)
1996-
(Type.effect (loc lam) [existentialp (loc lam) e] o)
2089+
o <- extendExistential Var.inferOutput
2090+
let f u t =
2091+
Type.arrow
2092+
(loc lam)
2093+
(expl u)
2094+
(Type.effect (loc lam) [] t)
2095+
pure $ foldr f (Type.effect (loc lam) [expl e] (expl o)) us
19972096
existentialFunctionTypeFor e = do
19982097
v <- extendExistential Var.inferOutput
19992098
pure $ existentialp (loc e) v
@@ -2004,6 +2103,38 @@ existentializeArrows t = do
20042103
t <- Type.existentializeArrows newVar t
20052104
pure t
20062105

2106+
-- Adds ability lists to arrows in a type that lack them. Unlike
2107+
-- `existentializeArrows`, this takes a count for a corresponding
2108+
-- repeated lambda, like
2109+
--
2110+
-- w x y z -> ...
2111+
--
2112+
-- since it is impossible for abilities to be used except after `z` in
2113+
-- such a case, this avoids making up variables for them. The `Int`
2114+
-- argument specifies how many repeated variables there are in the
2115+
-- corresponding term.
2116+
addAbilities :: (Var v) => Int -> Type v loc -> M v loc (Type v loc)
2117+
addAbilities ct ty
2118+
| a <- ABT.annotation ty,
2119+
Type.ForallsNamed' vs ty <- ty =
2120+
Type.foralls a vs <$> addAbilities ct ty
2121+
-- Note: Arrow'' matches even with no abilities, providing []
2122+
| ct > 1,
2123+
Type.Arrow'' a es b <- ty = do
2124+
a <- existentializeArrows a
2125+
b <- addAbilities (ct - 1) b
2126+
pure $ Type.arrow ann a (Type.effect ann es b)
2127+
| otherwise = existentializeArrows ty
2128+
where
2129+
ann = ABT.annotation ty
2130+
2131+
-- Counts the number of iterated lambdas in a term; for use with the
2132+
-- above `addAbilities`
2133+
countLambdas :: Term v loc -> Int
2134+
countLambdas tm
2135+
| Term.LamsNamed' vs _ <- tm = length vs
2136+
| otherwise = 0
2137+
20072138
ungeneralize :: (Var v, Ord loc) => Type v loc -> M v loc (Type v loc)
20082139
ungeneralize t = snd <$> ungeneralize' t
20092140

@@ -2213,7 +2344,7 @@ checkScoped e (Type.Forall' body) = do
22132344
want <- substAndDefaultWanted want pop
22142345
pure (generalizeP variableP pop ty, want)
22152346
checkScoped e t = do
2216-
t <- existentializeArrows t
2347+
t <- addAbilities (countLambdas e) t
22172348
(t,) <$> check e t
22182349

22192350
checkScopedWith ::

‎unison-src/transcripts-using-base/fix5129.output.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,15 @@ fancyTryEval = reraise << catchAll.impl
8989
9090
The check that
9191
92-
(Unit ->{IO, Exception} 𝕩88) ->{𝕖82, Exception, 𝕖89, IO} 𝕩88
92+
(Unit ->{IO, Exception} 𝕩84) ->{𝕖78, Exception, 𝕖85, IO} 𝕩84
9393
9494
is a subtype of
9595
96-
(Unit ->{g71, IO, Exception} a68) ->{g71, IO, Exception} a68
96+
(Unit ->{g69, IO, Exception} a66) ->{g69, IO, Exception} a66
9797
9898
failed because
9999
100-
{g71}
100+
{g69}
101101
102102
is not a subtype of
103103
Lines changed: 31 additions & 0 deletions

0 commit comments

Comments
 (0)