Skip to content

Commit a0c884b

Browse files
committed
refactor: alter type candidate, use it as an interface boundary
Instead of passing types and members separately to routines, we use type candidates as input to recursivity checks. This simplifies both validation and recursiveness checking on types and abstracts away differences in structure between sum type and product type members. I also had to adjust some test output, will restore them in a future commit.
1 parent 6ada67d commit a0c884b

12 files changed

+194
-159
lines changed

src/Concretize.hs

+18-26
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,8 @@ instantiateGenericStructType typeEnv env originalStructTy@(StructTy _ _) generic
613613
let nameFixedMembers = renameGenericTypeSymbolsOnProduct renamedOrig memberXObjs
614614
validMembers = replaceGenericTypeSymbolsOnMembers mappings' nameFixedMembers
615615
concretelyTypedMembers = replaceGenericTypeSymbolsOnMembers mappings memberXObjs
616-
candidate = TypeCandidate {restriction = AllowAnyTypeVariableNames, typename = (getStructName originalStructTy), typemembers = validMembers, variables = renamedOrig, interfaceConstraints = [], candidateTypeEnv = typeEnv, candidateEnv = env }
617-
validateMembers typeEnv env candidate
616+
candidate <- (fromDeftype (getStructName originalStructTy) renamedOrig typeEnv env validMembers)
617+
validateMembers (candidate {restriction = AllowAnyTypeVariableNames})
618618
deps <- mapM (depsForStructMemberPair typeEnv env) (pairwise concretelyTypedMembers)
619619
let xobj =
620620
XObj
@@ -642,30 +642,22 @@ instantiateGenericSumtype typeEnv env originalStructTy@(StructTy _ originalTyVar
642642
let fake1 = XObj (Sym (SymPath [] "a") Symbol) Nothing Nothing
643643
fake2 = XObj (Sym (SymPath [] "b") Symbol) Nothing Nothing
644644
rename@(StructTy _ renamedOrig) = evalState (renameVarTys originalStructTy) 0
645-
in case solve [Constraint rename genericStructTy fake1 fake2 fake1 OrdMultiSym] of
646-
Left e -> error (show e)
647-
Right mappings ->
648-
let nameFixedCases = map (renameGenericTypeSymbolsOnSum (zip originalTyVars renamedOrig)) cases
649-
concretelyTypedCases = map (replaceGenericTypeSymbolsOnCase mappings) nameFixedCases
650-
deps = mapM (depsForCase typeEnv env) concretelyTypedCases
651-
candidate = TypeCandidate {restriction = AllowAnyTypeVariableNames, typename = (getStructName originalStructTy), variables = renamedOrig, typemembers = concretelyTypedCases, interfaceConstraints = [], candidateTypeEnv = typeEnv, candidateEnv = env }
652-
in case toCases typeEnv env candidate of -- Don't care about the cases, this is done just for validation.
653-
Left err -> Left err
654-
Right _ ->
655-
case deps of
656-
Right okDeps ->
657-
Right $
658-
XObj
659-
( Lst
660-
( XObj (DefSumtype genericStructTy) Nothing Nothing :
661-
XObj (Sym (SymPath [] (tyToC genericStructTy)) Symbol) Nothing Nothing :
662-
concretelyTypedCases
663-
)
664-
)
665-
(Just dummyInfo)
666-
(Just TypeTy) :
667-
concat okDeps
668-
Left err -> Left err
645+
in do mappings <- replaceLeft (FailedToInstantiateGenericType originalStructTy) (solve [Constraint rename genericStructTy fake1 fake2 fake1 OrdMultiSym])
646+
let nameFixedCases = map (renameGenericTypeSymbolsOnSum (zip originalTyVars renamedOrig)) cases
647+
concretelyTypedCases = map (replaceGenericTypeSymbolsOnCase mappings) nameFixedCases
648+
candidate <- fromSumtype (getStructName originalStructTy) renamedOrig typeEnv env concretelyTypedCases
649+
_ <- toCases typeEnv env (candidate {restriction = AllowAnyTypeVariableNames})
650+
deps <- mapM (depsForCase typeEnv env) concretelyTypedCases
651+
pure (XObj
652+
( Lst
653+
( XObj (DefSumtype genericStructTy) Nothing Nothing :
654+
XObj (Sym (SymPath [] (tyToC genericStructTy)) Symbol) Nothing Nothing :
655+
concretelyTypedCases
656+
)
657+
)
658+
(Just dummyInfo)
659+
(Just TypeTy) :
660+
concat deps)
669661
instantiateGenericSumtype _ _ _ _ _ = error "instantiategenericsumtype"
670662

671663
-- Resolves dependencies for sumtype cases.

src/Deftype.hs

+5-8
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ moduleForDeftype innerEnv typeEnv env pathStrings typeName typeVariables rest i
6161
-- The variable 'insidePath' is the path used for all member functions inside the 'typeModule'.
6262
-- For example (module Vec2 [x Float]) creates bindings like Vec2.create, Vec2.x, etc.
6363
insidePath = pathStrings ++ [typeName]
64-
candidate = TypeCandidate {restriction = AllowOnlyNamesInScope, typename = typeName, variables = typeVariables, typemembers = [], interfaceConstraints = [], candidateTypeEnv = typeEnv, candidateEnv = env}
6564
initmembers = case rest of
6665
-- ANSI C does not allow empty structs. We add a dummy member here to account for this.
6766
-- Note that we *don't* add this member for external types--we leave those definitions up to the user.
@@ -75,11 +74,9 @@ moduleForDeftype innerEnv typeEnv env pathStrings typeName typeVariables rest i
7574
let structTy = StructTy (ConcreteNameTy (SymPath pathStrings typeName)) typeVariables
7675
ptrmembers = map (recursiveMembersToPointers structTy) rest
7776
ptrinitmembers = map (recursiveMembersToPointers structTy) initmembers
78-
innermems <- case ptrmembers of
79-
[XObj (Arr membersXObjs) _ _] -> Right membersXObjs
80-
_ -> Left $ NotAValidType (XObj (Sym (SymPath pathStrings typeName) Symbol) i (Just TypeTy))
81-
okRecursive (candidate {typemembers = mems})
82-
validateMembers typeEnv env (candidate {typemembers = innermems})
77+
candidate <- fromDeftype typeName typeVariables typeEnv env mems
78+
validateMembers candidate
79+
okRecursive candidate
8380
(okMembers, membersDeps) <- templatesForMembers typeEnv env insidePath structTy ptrmembers
8481
okInit <- if (any (isValueRecursive structTy) ptrmembers) then recursiveProductInitBinder insidePath structTy ptrinitmembers else binderForInit insidePath structTy initmembers
8582
okMake <- recursiveProductMakeBinder insidePath structTy ptrmembers
@@ -102,12 +99,12 @@ bindingsForRegisteredType typeEnv env pathStrings typeName rest i existingEnv =
10299
let moduleValueEnv = fromMaybe (new (Just env) (Just typeName)) (fmap fst existingEnv)
103100
moduleTypeEnv = fromMaybe (new (Just typeEnv) (Just typeName)) (fmap snd existingEnv)
104101
insidePath = pathStrings ++ [typeName]
105-
candidate = TypeCandidate {restriction = AllowOnlyNamesInScope, typename = typeName, variables = [], typemembers = [], interfaceConstraints = [], candidateTypeEnv = typeEnv, candidateEnv = env}
106102
in do
107103
mems <- case rest of
108104
[XObj (Arr membersXObjs) _ _] -> Right membersXObjs
109105
_ -> Left $ NotAValidType (XObj (Sym (SymPath pathStrings typeName) Symbol) i (Just TypeTy))
110-
validateMembers typeEnv env (candidate {typemembers = mems})
106+
candidate <- fromDeftype typeName [] typeEnv env mems
107+
validateMembers candidate
111108
let structTy = StructTy (ConcreteNameTy (SymPath pathStrings typeName)) []
112109
(binders, deps) <- templatesForMembers typeEnv env insidePath structTy rest
113110
okInit <- binderForInit insidePath structTy rest

src/Emit.hs

+16-12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ where
1515
import Control.Monad.State
1616
import Data.Char (ord)
1717
import Data.Functor ((<&>))
18+
import Data.Either (fromRight)
1819
import Data.List (intercalate, isPrefixOf, sortOn)
1920
import Data.Maybe (fromJust, fromMaybe, isJust)
2021
import Env
@@ -31,6 +32,7 @@ import Template
3132
import TypePredicates
3233
import Types
3334
import TypesToC
35+
import TypeCandidate
3436
import Util
3537

3638
addIndent :: Int -> String
@@ -843,48 +845,50 @@ memberToDecl recty indent (memberName, memberType) =
843845
Nothing -> error ("Invalid memberType: " ++ show memberType)
844846

845847
defStructToDeclaration :: Ty -> SymPath -> [XObj] -> String
846-
defStructToDeclaration structTy@(StructTy _ _) _ rest =
848+
defStructToDeclaration structTy@(StructTy _ vars) _ rest@[XObj (Arr mems) _ _] =
847849
let indent = indentAmount
848850
typedefCaseToMemberDecl :: XObj -> State EmitterState [()]
849851
-- ANSI C doesn't allow empty structs, insert a dummy member to keep the compiler happy.
850852
typedefCaseToMemberDecl (XObj (Arr []) _ _) = sequence $ pure $ appendToSrc (addIndent indent ++ "char __dummy;\n")
851853
typedefCaseToMemberDecl (XObj (Arr members) _ _) = mapM (memberToDecl structTy indent) (remove (isUnit . fromJust . xobjToTy . snd) (pairwise members))
852854
typedefCaseToMemberDecl _ = error "Invalid case in typedef."
853855
pointerfix = map (recursiveMembersToPointers structTy) rest
856+
candidate = fromDeftype (getStructName structTy) vars empty empty mems
857+
isRec = fromRight False (fmap isRecursive candidate)
854858
-- Note: the names of types are not namespaced
855859
visit = do
856860
-- forward declaration for recursive types.
857-
when (any (isRecursive structTy) pointerfix) $
861+
when isRec $
858862
do appendToSrc ("// Recursive type \n")
859863
appendToSrc ("struct " ++ tyToC structTy ++ " {\n")
860-
when (all (not . isRecursive structTy) pointerfix) $ appendToSrc "typedef struct {\n"
864+
when (not isRec) $ appendToSrc "typedef struct {\n"
861865
mapM_ typedefCaseToMemberDecl pointerfix
862866
appendToSrc "}"
863-
unless (any (isRecursive structTy) pointerfix)
864-
(appendToSrc (" " ++ tyToC structTy))
867+
unless isRec (appendToSrc (" " ++ tyToC structTy))
865868
appendToSrc ";\n"
866869
in if isTypeGeneric structTy
867870
then "" -- ("// " ++ show structTy ++ "\n")
868871
else emitterSrc (execState visit (EmitterState ""))
869872
defStructToDeclaration _ _ _ = error "defstructtodeclaration"
870873

871874
defSumtypeToDeclaration :: Ty -> [XObj] -> String
872-
defSumtypeToDeclaration sumTy@(StructTy _ _) rest =
875+
defSumtypeToDeclaration sumTy@(StructTy _ vars) rest =
873876
let indent = indentAmount
874877
pointerfix = map (recursiveMembersToPointers sumTy) rest
878+
candidate = fromSumtype (getStructName sumTy) vars empty empty rest
879+
isRec = (fromRight False (fmap isRecursive candidate))
875880
visit = do
876-
(if (any (isRecursive sumTy) pointerfix)
877-
then do appendToSrc ("// Recursive type \n")
878-
appendToSrc ("struct " ++ tyToC sumTy ++ " {\n")
879-
else appendToSrc "typedef struct {\n")
881+
if isRec
882+
then do appendToSrc ("// Recursive type \n")
883+
appendToSrc ("struct " ++ tyToC sumTy ++ " {\n")
884+
else appendToSrc "typedef struct {\n"
880885
appendToSrc (addIndent indent ++ "union {\n")
881886
mapM_ (emitSumtypeCase indent) pointerfix
882887
appendToSrc (addIndent indent ++ "char __dummy;\n")
883888
appendToSrc (addIndent indent ++ "} u;\n")
884889
appendToSrc (addIndent indent ++ "char _tag;\n")
885890
appendToSrc "}"
886-
unless (any (isRecursive sumTy) pointerfix)
887-
(appendToSrc (" " ++ tyToC sumTy))
891+
unless isRec (appendToSrc (" " ++ tyToC sumTy))
888892
appendToSrc ";\n"
889893
--appendToSrc ("// " ++ show typeVariables ++ "\n")
890894
mapM_ emitSumtypeCaseTagDefinition (zip [0 ..] pointerfix)

src/Primitives.hs

+9-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Data.List (foldl')
1515
import Data.Maybe (fromJust, fromMaybe)
1616
import Deftype
1717
import Emit
18-
import Env (addUsePath, contextEnv, insert, lookupBinderEverywhere, lookupEverywhere, lookupMeta, searchValueBinder)
18+
import Env (addUsePath, contextEnv, insert, lookupBinderEverywhere, lookupEverywhere, lookupMeta, searchValueBinder, empty)
1919
import EvalError
2020
import Infer
2121
import Info
@@ -35,6 +35,7 @@ import ToTemplate
3535
import TypeError
3636
import TypePredicates
3737
import Types
38+
import TypeCandidate
3839
import Util
3940
import Web.Browser (openBrowser)
4041
import RecType
@@ -647,8 +648,14 @@ makeType ctx name vars constructor =
647648
let qpath = (qualifyPath ctx (SymPath [] name))
648649
ty = StructTy (ConcreteNameTy (unqualify qpath)) vars
649650
(typeX, members, creator) = constructor ty
651+
mems = case members of
652+
[XObj (Arr xs) _ _] -> xs
653+
--(Lst xs) -> xs
654+
_ -> members
655+
candidate = fromDeftype name vars Env.empty Env.empty mems <> fromSumtype name vars Env.empty Env.empty mems
656+
isRec = fromRight False (fmap isRecursive candidate)
650657
-- if the type is recursive, tag it so we can easily find such types in the emitter.
651-
tBinder = if any (isRecursive ty) members
658+
tBinder = if isRec
652659
then Meta.updateBinderMeta (toBinder typeX) "recursive" trueXObj
653660
else (toBinder typeX)
654661
in case ( unwrapTypeErr ctx (creator ctx name vars members Nothing)

src/RecType.hs

+36-30
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
-- | Module RecType defines routines for working with recursive data types.
12
module RecType
23
(
34
recursiveMembersToPointers,
@@ -24,49 +25,54 @@ import Concretize
2425
import ToTemplate
2526
import Validate
2627

28+
-- | Returns true if a type candidate is recursive.
29+
isRecursive :: TypeCandidate -> Bool
30+
isRecursive candidate =
31+
let memberTypes = concat $ map snd (typemembers candidate)
32+
vars = variables candidate
33+
name = typename candidate
34+
in any (check name vars) memberTypes
35+
where check :: String -> [Ty] -> Ty -> Bool
36+
check name vars t = isDirectRecursion name vars t || isIndirectRecursion name vars t
37+
38+
isDirectRecursion :: String -> [Ty] -> Ty -> Bool
39+
isDirectRecursion name vars (StructTy (ConcreteNameTy (SymPath [] n)) rest) =
40+
(n == name && vars == rest)
41+
isDirectRecursion name vars (RecTy t) = isDirectRecursion name vars t
42+
isDirectRecursion _ _ _ = False
43+
44+
isIndirectRecursion :: String -> [Ty] -> Ty -> Bool
45+
isIndirectRecursion name vars t@(StructTy _ rest) =
46+
not (isDirectRecursion name vars t) && any (isDirectRecursion name vars) rest
47+
isIndirectRecursion name vars (PointerTy t) = isDirectRecursion name vars t
48+
isIndirectRecursion name vars (RefTy t _) = isDirectRecursion name vars t
49+
isIndirectRecursion _ _ _ = False
50+
2751
--------------------------------------------------------------------------------
2852
-- Base indirection recursion
2953

3054
-- | Returns true if a candidate type definition is a valid instance of recursivity.
3155
-- Types have valid recursion if they refer to themselves through indirection.
3256
okRecursive :: TypeCandidate -> Either TypeError ()
3357
okRecursive candidate =
34-
if any go (typemembers candidate)
35-
then validateInterfaceConstraints (candidate { interfaceConstraints = concat $ map go' (typemembers candidate)})
36-
else Right ()
37-
where go :: XObj -> Bool
38-
go (XObj (Sym (SymPath _ name) _) _ _) = name == typename candidate
39-
go (XObj (Lst xs) _ _) = any go xs
40-
go _ = False
41-
go' x@(XObj (Lst _) _ _) = if go x
42-
then case xobjToTy x of
43-
Just t@(PointerTy _) -> recInterfaceConstraints t
44-
Just t@(RefTy _ _) -> recInterfaceConstraints t
45-
Just t@(StructTy _ [_]) -> recInterfaceConstraints t
46-
_ -> []
47-
else []
48-
go' _ = []
58+
let name = typename candidate
59+
vars = variables candidate
60+
memberTypes = concat $ map snd (typemembers candidate)
61+
recursives = (filter (isIndirectRecursion name vars) memberTypes)
62+
ty = StructTy (ConcreteNameTy (SymPath [] name)) vars
63+
constraints = map (recInterfaceConstraints ty) recursives
64+
in validateInterfaceConstraints (candidate {interfaceConstraints = concat constraints})
4965

5066
-- | Generates interface constraints for a recursive type.
5167
-- The recursive portion of recursive types must be wrapped in a type F that supports indirection.
5268
-- We enforce this with two interfaces:
5369
-- allocate: Heap allocates a value T and wraps it in type F<T>
5470
-- indirect: Returns T from a heap allocated F<T>
55-
recInterfaceConstraints :: Ty -> [InterfaceConstraint]
56-
recInterfaceConstraints t =
57-
let members = tyMembers t
58-
in case members of
59-
[] -> []
60-
_ -> [ InterfaceConstraint "indirect" [(FuncTy [t] (head members) StaticLifetimeTy)],
61-
InterfaceConstraint "alloc" [(FuncTy [(head members)] t StaticLifetimeTy)]
62-
]
63-
64-
-- | Returns true if a type member xobj is recursive (either through indirect recursion or "value" recursion)
65-
isRecursive :: Ty -> XObj -> Bool
66-
isRecursive (StructTy (ConcreteNameTy spath) []) (XObj (Sym path _) _ _) = spath == path
67-
isRecursive rec (XObj (Lst xs) _ _) = any (isRecursive rec) xs
68-
isRecursive rec (XObj (Arr xs) _ _) = any (isRecursive rec) xs
69-
isRecursive _ _ = False
71+
recInterfaceConstraints :: Ty -> Ty -> [InterfaceConstraint]
72+
recInterfaceConstraints recTy t =
73+
[ InterfaceConstraint "indirect" [(FuncTy [t] recTy StaticLifetimeTy)],
74+
InterfaceConstraint "alloc" [(FuncTy [recTy] t StaticLifetimeTy)]
75+
]
7076

7177
--------------------------------------------------------------------------------
7278
-- **Value recursion sugar**

src/SumtypeCase.hs

+11-25
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,14 @@ data SumtypeCase = SumtypeCase
1515
toCases :: TypeEnv -> Env -> TypeCandidate -> Either TypeError [SumtypeCase]
1616
toCases typeEnv globalEnv candidate = mapM (toCase (typename candidate) typeEnv globalEnv (restriction candidate) (variables candidate)) (typemembers candidate)
1717

18-
toCase :: String -> TypeEnv -> Env -> TypeVarRestriction -> [Ty] -> XObj -> Either TypeError SumtypeCase
19-
toCase tyname typeEnv globalEnv varrestriction typeVars x@(XObj (Lst [XObj (Sym (SymPath [] pname) Symbol) _ _, XObj (Arr tyXObjs) _ _]) _ _) =
20-
let tys = map xobjToTy tyXObjs
21-
in case sequence tys of
22-
Nothing ->
23-
Left (InvalidSumtypeCase x)
24-
Just okTys ->
25-
let validated = map (\t -> canBeUsedAsMemberType tyname varrestriction typeEnv globalEnv typeVars t x) okTys
26-
in case sequence validated of
27-
Left e ->
28-
Left e
29-
Right _ ->
30-
Right $
31-
SumtypeCase
32-
{ caseName = pname,
33-
caseTys = okTys
34-
}
35-
toCase _ _ _ _ _ (XObj (Sym (SymPath [] pname) Symbol) _ _) =
36-
Right $
37-
SumtypeCase
38-
{ caseName = pname,
39-
caseTys = []
40-
}
41-
toCase _ _ _ _ _ x =
42-
Left (InvalidSumtypeCase x)
18+
toCase :: String -> TypeEnv -> Env -> TypeVarRestriction -> [Ty] -> (String, [Ty]) -> Either TypeError SumtypeCase
19+
toCase tyname typeEnv globalEnv varrestriction typeVars member =
20+
let validated = mapM (\t -> canBeUsedAsMemberType tyname varrestriction typeEnv globalEnv typeVars t) (snd member)
21+
in case validated of
22+
Left e -> Left e
23+
Right _ ->
24+
Right $
25+
SumtypeCase
26+
{ caseName = fst member,
27+
caseTys = snd member
28+
}

src/Sumtypes.hs

+3-2
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@ moduleForSumtype innerEnv typeEnv env pathStrings typeName typeVariables rest i
5353
let moduleValueEnv = fromMaybe (new innerEnv (Just typeName)) (fmap fst existingEnv)
5454
moduleTypeEnv = fromMaybe (new (Just typeEnv) (Just typeName)) (fmap snd existingEnv)
5555
insidePath = pathStrings ++ [typeName]
56-
candidate = TypeCandidate {typename = typeName, variables = typeVariables, restriction = AllowOnlyNamesInScope, typemembers = rest, interfaceConstraints = [], candidateTypeEnv = typeEnv, candidateEnv = env}
5756
in do
5857
let structTy = StructTy (ConcreteNameTy (SymPath pathStrings typeName)) typeVariables
5958
ptrFix = map (recursiveMembersToPointers structTy) rest
59+
candidate <- fromSumtype typeName typeVariables typeEnv env rest
6060
okRecursive candidate
61-
cases <- toCases typeEnv env (candidate {typemembers = ptrFix})
61+
candidate' <- fromSumtype typeName typeVariables typeEnv env ptrFix
62+
cases <- toCases typeEnv env candidate'
6263
okIniters <- initers insidePath structTy cases
6364
okTag <- binderForTag insidePath structTy
6465
(okStr, okStrDeps) <- binderForStrOrPrn typeEnv env insidePath structTy cases "str"

0 commit comments

Comments
 (0)