Skip to content

Commit df39477

Browse files
committed
refactor: add a data type for type validation
This makes it easier to work with validation functions at call sites as well as paves the way for permitting recursive types (we pass along the type name to validation procedures).
1 parent 5f01d64 commit df39477

File tree

5 files changed

+99
-68
lines changed

5 files changed

+99
-68
lines changed

src/Concretize.hs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,8 @@ instantiateGenericStructType typeEnv env originalStructTy@(StructTy _ _) generic
612612
let nameFixedMembers = renameGenericTypeSymbolsOnProduct renamedOrig memberXObjs
613613
validMembers = replaceGenericTypeSymbolsOnMembers mappings' nameFixedMembers
614614
concretelyTypedMembers = replaceGenericTypeSymbolsOnMembers mappings memberXObjs
615-
validateMembers AllowAnyTypeVariableNames typeEnv env renamedOrig validMembers
615+
candidate = TypeCandidate {restriction = AllowAnyTypeVariableNames, typename = (getStructName originalStructTy), typemembers = validMembers, variables = renamedOrig}
616+
validateMembers typeEnv env candidate
616617
deps <- mapM (depsForStructMemberPair typeEnv env) (pairwise concretelyTypedMembers)
617618
let xobj =
618619
XObj
@@ -646,7 +647,8 @@ instantiateGenericSumtype typeEnv env originalStructTy@(StructTy _ originalTyVar
646647
let nameFixedCases = map (renameGenericTypeSymbolsOnSum (zip originalTyVars renamedOrig)) cases
647648
concretelyTypedCases = map (replaceGenericTypeSymbolsOnCase mappings) nameFixedCases
648649
deps = mapM (depsForCase typeEnv env) concretelyTypedCases
649-
in case toCases typeEnv env AllowAnyTypeVariableNames renamedOrig concretelyTypedCases of -- Don't care about the cases, this is done just for validation.
650+
candidate = TypeCandidate {restriction = AllowAnyTypeVariableNames, typename = (getStructName originalStructTy), variables = renamedOrig, typemembers = concretelyTypedCases }
651+
in case toCases typeEnv env candidate of -- Don't care about the cases, this is done just for validation.
650652
Left err -> Left err
651653
Right _ ->
652654
case deps of

src/Deftype.hs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,13 @@ moduleForDeftype innerEnv typeEnv env pathStrings typeName typeVariables rest i
5959
-- The variable 'insidePath' is the path used for all member functions inside the 'typeModule'.
6060
-- For example (module Vec2 [x Float]) creates bindings like Vec2.create, Vec2.x, etc.
6161
insidePath = pathStrings ++ [typeName]
62+
candidate = TypeCandidate {restriction = AllowOnlyNamesInScope, typename = typeName, variables = typeVariables, typemembers = []}
6263
in do
63-
validateMemberCases typeEnv env typeVariables rest
64+
mems <- case rest of
65+
[XObj (Arr membersXObjs) _ _] -> Right membersXObjs
66+
_ -> Left $ NotAValidType (XObj (Sym (SymPath pathStrings typeName) Symbol) i (Just TypeTy))
67+
validateMembers typeEnv env (candidate {typemembers = mems})
68+
--validateMemberCases typeEnv env typeVariables rest
6469
let structTy = StructTy (ConcreteNameTy (SymPath pathStrings typeName)) typeVariables
6570
(okMembers, membersDeps) <- templatesForMembers typeEnv env insidePath structTy rest
6671
okInit <- binderForInit insidePath structTy rest
@@ -82,8 +87,12 @@ bindingsForRegisteredType typeEnv env pathStrings typeName rest i existingEnv =
8287
let moduleValueEnv = fromMaybe (new (Just env) (Just typeName)) (fmap fst existingEnv)
8388
moduleTypeEnv = fromMaybe (new (Just typeEnv) (Just typeName)) (fmap snd existingEnv)
8489
insidePath = pathStrings ++ [typeName]
90+
candidate = TypeCandidate {restriction = AllowOnlyNamesInScope, typename = typeName, variables = [], typemembers = []}
8591
in do
86-
validateMemberCases typeEnv env [] rest
92+
mems <- case rest of
93+
[XObj (Arr membersXObjs) _ _] -> Right membersXObjs
94+
_ -> Left $ NotAValidType (XObj (Sym (SymPath pathStrings typeName) Symbol) i (Just TypeTy))
95+
validateMembers typeEnv env (candidate {typemembers = mems})
8796
let structTy = StructTy (ConcreteNameTy (SymPath pathStrings typeName)) []
8897
(binders, deps) <- templatesForMembers typeEnv env insidePath structTy rest
8998
okInit <- binderForInit insidePath structTy rest

src/SumtypeCase.hs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,31 @@ data SumtypeCase = SumtypeCase
1111
}
1212
deriving (Show, Eq)
1313

14-
toCases :: TypeEnv -> Env -> TypeVarRestriction -> [Ty] -> [XObj] -> Either TypeError [SumtypeCase]
15-
toCases typeEnv globalEnv restriction typeVars = mapM (toCase typeEnv globalEnv restriction typeVars)
14+
toCases :: TypeEnv -> Env -> TypeCandidate -> Either TypeError [SumtypeCase]
15+
toCases typeEnv globalEnv candidate = mapM (toCase (typename candidate) typeEnv globalEnv (restriction candidate) (variables candidate)) (typemembers candidate)
1616

17-
toCase :: TypeEnv -> Env -> TypeVarRestriction -> [Ty] -> XObj -> Either TypeError SumtypeCase
18-
toCase typeEnv globalEnv restriction typeVars x@(XObj (Lst [XObj (Sym (SymPath [] name) Symbol) _ _, XObj (Arr tyXObjs) _ _]) _ _) =
17+
toCase :: String -> TypeEnv -> Env -> TypeVarRestriction -> [Ty] -> XObj -> Either TypeError SumtypeCase
18+
toCase tyname typeEnv globalEnv varrestriction typeVars x@(XObj (Lst [XObj (Sym (SymPath [] pname) Symbol) _ _, XObj (Arr tyXObjs) _ _]) _ _) =
1919
let tys = map xobjToTy tyXObjs
2020
in case sequence tys of
2121
Nothing ->
2222
Left (InvalidSumtypeCase x)
2323
Just okTys ->
24-
let validated = map (\t -> canBeUsedAsMemberType restriction typeEnv globalEnv typeVars t x) okTys
24+
let validated = map (\t -> canBeUsedAsMemberType tyname varrestriction typeEnv globalEnv typeVars t x) okTys
2525
in case sequence validated of
2626
Left e ->
2727
Left e
2828
Right _ ->
2929
Right $
3030
SumtypeCase
31-
{ caseName = name,
31+
{ caseName = pname,
3232
caseTys = okTys
3333
}
34-
toCase _ _ _ _ (XObj (Sym (SymPath [] name) Symbol) _ _) =
34+
toCase _ _ _ _ _ (XObj (Sym (SymPath [] pname) Symbol) _ _) =
3535
Right $
3636
SumtypeCase
37-
{ caseName = name,
37+
{ caseName = pname,
3838
caseTys = []
3939
}
40-
toCase _ _ _ _ x =
40+
toCase _ _ _ _ _ x =
4141
Left (InvalidSumtypeCase x)

src/Sumtypes.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import TypePredicates
1717
import Types
1818
import TypesToC
1919
import Util
20-
import Validate (TypeVarRestriction (..))
20+
import Validate (TypeVarRestriction (..), TypeCandidate (..))
2121

2222
getCase :: [SumtypeCase] -> String -> Maybe SumtypeCase
2323
getCase cases caseNameToFind =
@@ -52,9 +52,10 @@ moduleForSumtype innerEnv typeEnv env pathStrings typeName typeVariables rest i
5252
let moduleValueEnv = fromMaybe (new innerEnv (Just typeName)) (fmap fst existingEnv)
5353
moduleTypeEnv = fromMaybe (new (Just typeEnv) (Just typeName)) (fmap snd existingEnv)
5454
insidePath = pathStrings ++ [typeName]
55+
candidate = TypeCandidate {typename = typeName, variables = typeVariables, restriction = AllowOnlyNamesInScope, typemembers = rest}
5556
in do
5657
let structTy = StructTy (ConcreteNameTy (SymPath pathStrings typeName)) typeVariables
57-
cases <- toCases typeEnv env AllowOnlyNamesInScope typeVariables rest
58+
cases <- toCases typeEnv env candidate
5859
okIniters <- initers insidePath structTy cases
5960
okTag <- binderForTag insidePath structTy
6061
(okStr, okStrDeps) <- binderForStrOrPrn typeEnv env insidePath structTy cases "str"

src/Validate.hs

Lines changed: 72 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18,60 +18,79 @@ data TypeVarRestriction
1818
| AllowOnlyNamesInScope -- Used when checking a type definition, e.g. (deftype (Foo a) [x a]), requires a to be in scope
1919
deriving (Eq)
2020

21+
-- | TypeCandidate represents a type that's possibly valid or invalid.
22+
data TypeCandidate = TypeCandidate {
23+
-- the name of the type
24+
typename :: String,
25+
-- a list of all variables in the type head
26+
variables :: [Ty],
27+
-- all members of the type
28+
typemembers :: [XObj],
29+
-- what sort of type variables are permitted.
30+
restriction :: TypeVarRestriction
31+
}
32+
2133
-- | Make sure that the member declarations in a type definition
2234
-- | Follow the pattern [<name> <type>, <name> <type>, ...]
2335
-- | TODO: This function is only called by the deftype parts of the codebase, which is more specific than the following check implies.
24-
validateMemberCases :: TypeEnv -> Env -> [Ty] -> [XObj] -> Either TypeError ()
25-
validateMemberCases typeEnv globalEnv typeVariables rest = mapM_ visit rest
36+
validateMemberCases :: TypeEnv -> Env -> TypeCandidate -> Either TypeError ()
37+
validateMemberCases typeEnv globalEnv candidate = --mapM_ visit (members candidate)
38+
validateMembers typeEnv globalEnv (candidate {restriction = AllowOnlyNamesInScope})
39+
-- where
40+
-- visit (XObj (Arr membersXObjs) _ _) =
41+
-- validateMembers typeEnv globalEnv (candidate {restriction = AllowOnlyNamesInScope})
42+
-- visit xobj =
43+
-- Left (InvalidSumtypeCase xobj)
44+
45+
validateMembers :: TypeEnv -> Env -> TypeCandidate -> Either TypeError ()
46+
validateMembers typeEnv globalEnv candidate =
47+
(checkUnevenMembers candidate) >>
48+
(checkDuplicateMembers candidate) >>
49+
(checkMembers typeEnv globalEnv candidate) >>
50+
(checkKindConsistency candidate)
51+
52+
-- | Returns an error if a type has an uneven number of members.
53+
checkUnevenMembers :: TypeCandidate -> Either TypeError ()
54+
checkUnevenMembers candidate =
55+
if even (length (typemembers candidate))
56+
then Right ()
57+
else Left (UnevenMembers (typemembers candidate))
58+
59+
-- | Returns an error if a type has more than one member with the same name.
60+
checkDuplicateMembers :: TypeCandidate -> Either TypeError ()
61+
checkDuplicateMembers candidate =
62+
if length fields == length uniqueFields
63+
then Right ()
64+
else Left (DuplicatedMembers dups)
2665
where
27-
visit (XObj (Arr membersXObjs) _ _) =
28-
validateMembers AllowOnlyNamesInScope typeEnv globalEnv typeVariables membersXObjs
29-
visit xobj =
30-
Left (InvalidSumtypeCase xobj)
66+
fields = fst <$> (pairwise (typemembers candidate))
67+
uniqueFields = nubBy ((==) `on` xobjObj) fields
68+
dups = fields \\ uniqueFields
3169

32-
validateMembers :: TypeVarRestriction -> TypeEnv -> Env -> [Ty] -> [XObj] -> Either TypeError ()
33-
validateMembers typeVarRestriction typeEnv globalEnv typeVariables membersXObjs =
34-
checkUnevenMembers >> checkDuplicateMembers >> checkMembers >> checkKindConsistency
70+
-- | Returns an error if the type variables in the body of the type and variables in the head of the type are of incompatible kinds.
71+
checkKindConsistency :: TypeCandidate -> Either TypeError ()
72+
checkKindConsistency candidate =
73+
case areKindsConsistent varsOnly of
74+
Left var -> Left (InconsistentKinds var (typemembers candidate))
75+
_ -> pure ()
3576
where
36-
pairs = pairwise membersXObjs
37-
-- Are the number of members even?
38-
checkUnevenMembers :: Either TypeError ()
39-
checkUnevenMembers =
40-
if even (length membersXObjs)
41-
then Right ()
42-
else Left (UnevenMembers membersXObjs)
43-
-- Are any members duplicated?
44-
checkDuplicateMembers :: Either TypeError ()
45-
checkDuplicateMembers =
46-
if length fields == length uniqueFields
47-
then Right ()
48-
else Left (DuplicatedMembers dups)
49-
where
50-
fields = fst <$> pairs
51-
uniqueFields = nubBy ((==) `on` xobjObj) fields
52-
dups = fields \\ uniqueFields
53-
-- Do all type variables have consistent kinds?
54-
checkKindConsistency :: Either TypeError ()
55-
checkKindConsistency =
56-
case areKindsConsistent varsOnly of
57-
Left var -> Left (InconsistentKinds var membersXObjs)
58-
_ -> pure ()
59-
where
60-
-- fromJust is safe here; invalid types will be caught in the prior check.
61-
-- todo? be safer anyway?
62-
varsOnly = filter isTypeGeneric (map (fromJust . xobjToTy . snd) pairs)
63-
checkMembers :: Either TypeError ()
64-
checkMembers = mapM_ (okXObjForType typeVarRestriction typeEnv globalEnv typeVariables . snd) pairs
77+
-- fromJust is safe here; invalid types will be caught in a prior check.
78+
-- TODO: be safer.
79+
varsOnly = filter isTypeGeneric (map (fromJust . xobjToTy . snd) (pairwise (typemembers candidate)))
80+
81+
-- | Returns an error if one of the types members can't be used as a member.
82+
checkMembers :: TypeEnv -> Env -> TypeCandidate -> Either TypeError ()
83+
checkMembers typeEnv globalEnv candidate = mapM_ (okXObjForType (typename candidate) (restriction candidate) typeEnv globalEnv (variables candidate) . snd) (pairwise (typemembers candidate))
6584

66-
okXObjForType :: TypeVarRestriction -> TypeEnv -> Env -> [Ty] -> XObj -> Either TypeError ()
67-
okXObjForType typeVarRestriction typeEnv globalEnv typeVariables xobj =
85+
okXObjForType :: String -> TypeVarRestriction -> TypeEnv -> Env -> [Ty] -> XObj -> Either TypeError ()
86+
okXObjForType tyname typeVarRestriction typeEnv globalEnv typeVariables xobj =
6887
case xobjToTy xobj of
69-
Just t -> canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables t xobj
88+
Just t -> canBeUsedAsMemberType tyname typeVarRestriction typeEnv globalEnv typeVariables t xobj
7089
Nothing -> Left (NotAType xobj)
7190

7291
-- | Can this type be used as a member for a deftype?
73-
canBeUsedAsMemberType :: TypeVarRestriction -> TypeEnv -> Env -> [Ty] -> Ty -> XObj -> Either TypeError ()
74-
canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables ty xobj =
92+
canBeUsedAsMemberType :: String -> TypeVarRestriction -> TypeEnv -> Env -> [Ty] -> Ty -> XObj -> Either TypeError ()
93+
canBeUsedAsMemberType tyname typeVarRestriction typeEnv globalEnv typeVariables ty xobj =
7594
case ty of
7695
UnitTy -> pure ()
7796
IntTy -> pure ()
@@ -86,7 +105,7 @@ canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables ty xobj
86105
FuncTy {} -> pure ()
87106
PointerTy UnitTy -> pure ()
88107
PointerTy inner ->
89-
canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables inner xobj
108+
canBeUsedAsMemberType tyname typeVarRestriction typeEnv globalEnv typeVariables inner xobj
90109
>> pure ()
91110
-- Struct variables may appear as complete applications or individual
92111
-- components in the head of a definition; that is the forms:
@@ -105,23 +124,23 @@ canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables ty xobj
105124
-- ((Foo (f a) (f b)) ...)
106125
-- differ.
107126
-- Attempt the first, more restrictive formulation first.
108-
struct@(StructTy name tyVars) ->
109-
checkVar struct <> checkStruct name tyVars
127+
struct@(StructTy sname tyVars) ->
128+
checkVar struct <> checkStruct sname tyVars
110129
v@(VarTy _) -> checkVar v
111130
_ -> Left (InvalidMemberType ty xobj)
112131
where
113132
checkStruct :: Ty -> [Ty] -> Either TypeError ()
114133
checkStruct (ConcreteNameTy (SymPath [] "Array")) [innerType] =
115-
canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables innerType xobj
134+
canBeUsedAsMemberType tyname typeVarRestriction typeEnv globalEnv typeVariables innerType xobj
116135
>> pure ()
117-
checkStruct (ConcreteNameTy path@(SymPath _ name)) vars =
118-
case E.getTypeBinder typeEnv name <> E.findTypeBinder globalEnv path of
136+
checkStruct (ConcreteNameTy path@(SymPath _ pname)) vars =
137+
case E.getTypeBinder typeEnv pname <> E.findTypeBinder globalEnv path of
119138
Right (Binder _ (XObj (Lst (XObj (ExternalType _) _ _ : _)) _ _)) ->
120139
pure ()
121140
Right (Binder _ (XObj (Lst (XObj (Deftype t) _ _ : _)) _ _)) ->
122-
checkInhabitants t >> foldM (\_ typ -> canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables typ xobj) () vars
141+
checkInhabitants t >> foldM (\_ typ -> canBeUsedAsMemberType tyname typeVarRestriction typeEnv globalEnv typeVariables typ xobj) () vars
123142
Right (Binder _ (XObj (Lst (XObj (DefSumtype t) _ _ : _)) _ _)) ->
124-
checkInhabitants t >> foldM (\_ typ -> canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables typ xobj) () vars
143+
checkInhabitants t >> foldM (\_ typ -> canBeUsedAsMemberType tyname typeVarRestriction typeEnv globalEnv typeVariables typ xobj) () vars
125144
_ -> Left (NotAmongRegisteredTypes ty xobj)
126145
where
127146
checkInhabitants :: Ty -> Either TypeError ()
@@ -131,8 +150,8 @@ canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables ty xobj
131150
else Left (UninhabitedConstructor ty xobj (length vs) (length vars))
132151
checkInhabitants _ = Left (InvalidMemberType ty xobj)
133152
checkStruct v@(VarTy _) vars =
134-
canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables v xobj
135-
>> foldM (\_ typ -> canBeUsedAsMemberType typeVarRestriction typeEnv globalEnv typeVariables typ xobj) () vars
153+
canBeUsedAsMemberType tyname typeVarRestriction typeEnv globalEnv typeVariables v xobj
154+
>> foldM (\_ typ -> canBeUsedAsMemberType tyname typeVarRestriction typeEnv globalEnv typeVariables typ xobj) () vars
136155
checkStruct _ _ = error "checkstruct"
137156
checkVar :: Ty -> Either TypeError ()
138157
checkVar variable =

0 commit comments

Comments
 (0)