2727use PackageFactory \ComponentEngine \TypeSystem \Resolver \Expression \ExpressionTypeResolver ;
2828use PackageFactory \ComponentEngine \TypeSystem \ScopeInterface ;
2929use PackageFactory \ComponentEngine \TypeSystem \Type \BooleanType \BooleanType ;
30- use PackageFactory \ComponentEngine \TypeSystem \Type \EnumType \EnumType ;
30+ use PackageFactory \ComponentEngine \TypeSystem \Type \EnumType \EnumInstanceType ;
3131use PackageFactory \ComponentEngine \TypeSystem \Type \UnionType \UnionType ;
3232use PackageFactory \ComponentEngine \TypeSystem \TypeInterface ;
3333
@@ -67,7 +67,14 @@ private function resolveTypeOfBooleanMatch(MatchNode $matchNode): TypeInterface
6767 } else {
6868 $ types = [];
6969
70+ $ defaultArmPresent = false ;
7071 foreach ($ matchNode ->arms ->items as $ matchArmNode ) {
72+ if ($ defaultArmPresent ) {
73+ throw new \Exception ('@TODO: Multiple illegal default arms ' );
74+ }
75+ if ($ matchArmNode ->left === null ) {
76+ $ defaultArmPresent = true ;
77+ }
7178 $ types [] = $ expressionTypeResolver ->resolveTypeOf (
7279 $ matchArmNode ->right
7380 );
@@ -79,20 +86,57 @@ private function resolveTypeOfBooleanMatch(MatchNode $matchNode): TypeInterface
7986 }
8087 }
8188
82- private function resolveTypeOfEnumMatch (MatchNode $ matchNode ): TypeInterface
89+ private function resolveTypeOfEnumMatch (MatchNode $ matchNode, EnumInstanceType $ subjectEnumType ): TypeInterface
8390 {
8491 $ expressionTypeResolver = new ExpressionTypeResolver (
8592 scope: $ this ->scope
8693 );
8794 $ types = [];
8895
96+ $ defaultArmPresent = false ;
97+ $ referencedEnumMembers = [];
98+
8999 foreach ($ matchNode ->arms ->items as $ matchArmNode ) {
100+ if ($ defaultArmPresent ) {
101+ throw new \Exception ('@TODO Error: Multiple illegal default arms ' );
102+ }
103+ if ($ matchArmNode ->left === null ) {
104+ $ defaultArmPresent = true ;
105+ } else {
106+ foreach ($ matchArmNode ->left ->items as $ expressionNode ) {
107+ $ enumMemberType = $ expressionTypeResolver ->resolveTypeOf ($ expressionNode );
108+ if (!$ enumMemberType instanceof EnumInstanceType) {
109+ throw new \Error ('@TODO Error: Cannot match enum with type of ' . $ enumMemberType ::class);
110+ }
111+
112+ if ($ enumMemberType ->isUnspecified ()) {
113+ throw new \Error ('@TODO Error: Matching enum value should be referenced statically ' );
114+ }
115+
116+ if (!$ enumMemberType ->enumStaticType ->is ($ subjectEnumType ->enumStaticType )) {
117+ throw new \Error ('@TODO Error: incompatible enum match: got ' . $ enumMemberType ->enumStaticType ->enumName . ' expected ' . $ subjectEnumType ->enumStaticType ->enumName );
118+ }
119+
120+ if (isset ($ referencedEnumMembers [$ enumMemberType ->getMemberName ()])) {
121+ throw new \Error ('@TODO Error: Enum path ' . $ enumMemberType ->getMemberName () . ' was already defined once in this match and cannot be used twice ' );
122+ }
123+
124+ $ referencedEnumMembers [$ enumMemberType ->getMemberName ()] = true ;
125+ }
126+ }
127+
90128 $ types [] = $ expressionTypeResolver ->resolveTypeOf (
91129 $ matchArmNode ->right
92130 );
93131 }
94132
95- // @TODO: Ensure that match is complete
133+ if (!$ defaultArmPresent ) {
134+ foreach ($ subjectEnumType ->enumStaticType ->getMemberNames () as $ member ) {
135+ if (!isset ($ referencedEnumMembers [$ member ])) {
136+ throw new \Error ('@TODO Error: member ' . $ member . ' not checked ' );
137+ }
138+ }
139+ }
96140
97141 return UnionType::of (...$ types );
98142 }
@@ -108,7 +152,7 @@ public function resolveTypeOf(MatchNode $matchNode): TypeInterface
108152
109153 return match (true ) {
110154 BooleanType::get ()->is ($ typeOfSubject ) => $ this ->resolveTypeOfBooleanMatch ($ matchNode ),
111- $ typeOfSubject instanceof EnumType => $ this ->resolveTypeOfEnumMatch ($ matchNode ),
155+ $ typeOfSubject instanceof EnumInstanceType => $ this ->resolveTypeOfEnumMatch ($ matchNode, $ typeOfSubject ),
112156 default => throw new \Exception ('@TODO: Not handled ' . $ typeOfSubject ::class)
113157 };
114158 }
0 commit comments