99
1010use std:: collections:: HashMap ;
1111
12- use codemap_diagnostic:: Diagnostic ;
12+ use codemap_diagnostic:: { Diagnostic , Level , SpanLabel , SpanStyle } ;
1313use core_model:: mapped_arena:: MappedArena ;
1414use core_model_builder:: typechecker:: { Typed , annotation:: AnnotationSpec } ;
1515
16- use crate :: ast:: ast_types:: { AstExpr , FieldSelection , LogicalOp , RelationalOp , Untyped } ;
16+ use crate :: ast:: ast_types:: {
17+ AstExpr , FieldSelection , FieldSelectionElement , LogicalOp , RelationalOp , Untyped ,
18+ } ;
1719
1820use super :: { Scope , Type , TypecheckFrom } ;
1921
@@ -27,6 +29,9 @@ impl TypecheckFrom<AstExpr<Untyped>> for AstExpr<Typed> {
2729 AstExpr :: RelationalOp ( relation) => {
2830 AstExpr :: RelationalOp ( RelationalOp :: shallow ( relation) )
2931 }
32+ AstExpr :: EnumLiteral ( enum_name, value, span, _) => {
33+ AstExpr :: EnumLiteral ( enum_name. clone ( ) , value. clone ( ) , * span, Type :: Defer )
34+ }
3035 AstExpr :: StringLiteral ( v, s) => AstExpr :: StringLiteral ( v. clone ( ) , * s) ,
3136 AstExpr :: BooleanLiteral ( v, s) => AstExpr :: BooleanLiteral ( * v, * s) ,
3237 AstExpr :: NumberLiteral ( v, s) => AstExpr :: NumberLiteral ( v. clone ( ) , * s) ,
@@ -49,11 +54,39 @@ impl TypecheckFrom<AstExpr<Untyped>> for AstExpr<Typed> {
4954 errors : & mut Vec < Diagnostic > ,
5055 ) -> bool {
5156 match self {
52- AstExpr :: FieldSelection ( select) => select. pass ( type_env, annotation_env, scope, errors) ,
57+ AstExpr :: FieldSelection ( select) => {
58+ if let Some ( ( enum_name, value, span) ) = enum_literal_parts ( select) {
59+ if let Some ( typ) =
60+ resolve_enum_literal ( & enum_name, & value, span, type_env, scope, errors)
61+ {
62+ * self = AstExpr :: EnumLiteral ( enum_name, value, span, typ) ;
63+ true
64+ } else {
65+ select. pass ( type_env, annotation_env, scope, errors)
66+ }
67+ } else {
68+ select. pass ( type_env, annotation_env, scope, errors)
69+ }
70+ }
5371 AstExpr :: LogicalOp ( logic) => logic. pass ( type_env, annotation_env, scope, errors) ,
5472 AstExpr :: RelationalOp ( relation) => {
5573 relation. pass ( type_env, annotation_env, scope, errors)
5674 }
75+ AstExpr :: EnumLiteral ( enum_name, value, span, typ) => {
76+ if typ. is_incomplete ( ) {
77+ if let Some ( resolved) =
78+ resolve_enum_literal ( enum_name, value, * span, type_env, scope, errors)
79+ {
80+ * typ = resolved;
81+ true
82+ } else {
83+ * typ = Type :: Error ;
84+ false
85+ }
86+ } else {
87+ false
88+ }
89+ }
5790 AstExpr :: StringList ( _, _)
5891 | AstExpr :: StringLiteral ( _, _)
5992 | AstExpr :: BooleanLiteral ( _, _)
@@ -63,3 +96,76 @@ impl TypecheckFrom<AstExpr<Untyped>> for AstExpr<Typed> {
6396 }
6497 }
6598}
99+
100+ fn enum_literal_parts (
101+ selection : & FieldSelection < Typed > ,
102+ ) -> Option < ( String , String , codemap:: Span ) > {
103+ match selection {
104+ FieldSelection :: Select ( prefix, elem, span, _) => match ( prefix. as_ref ( ) , elem) {
105+ (
106+ FieldSelection :: Single ( prefix_elem, _) ,
107+ FieldSelectionElement :: Identifier ( value, _, _) ,
108+ ) => {
109+ if let FieldSelectionElement :: Identifier ( enum_name, _, _) = prefix_elem {
110+ Some ( ( enum_name. clone ( ) , value. clone ( ) , * span) )
111+ } else {
112+ None
113+ }
114+ }
115+ _ => None ,
116+ } ,
117+ _ => None ,
118+ }
119+ }
120+
121+ fn resolve_enum_literal (
122+ enum_name : & str ,
123+ value : & str ,
124+ span : codemap:: Span ,
125+ type_env : & MappedArena < Type > ,
126+ scope : & Scope ,
127+ errors : & mut Vec < Diagnostic > ,
128+ ) -> Option < Type > {
129+ if scope. get_type ( enum_name) . is_some ( ) {
130+ return None ;
131+ }
132+
133+ let is_context = type_env
134+ . get_by_key ( enum_name)
135+ . and_then ( |t| match t {
136+ Type :: Composite ( c) if c. kind == crate :: ast:: ast_types:: AstModelKind :: Context => Some ( c) ,
137+ _ => None ,
138+ } )
139+ . is_some ( ) ;
140+
141+ if is_context {
142+ return None ;
143+ }
144+
145+ let enum_type = type_env. get_by_key ( enum_name) . and_then ( |t| match t {
146+ Type :: Enum ( e) => Some ( e. clone ( ) ) ,
147+ _ => None ,
148+ } ) ;
149+
150+ #[ allow( clippy:: question_mark) ]
151+ let enum_type = match enum_type {
152+ Some ( enum_type) => enum_type,
153+ None => return None ,
154+ } ;
155+
156+ if enum_type. fields . iter ( ) . any ( |f| f. name == value) {
157+ Some ( Type :: Enum ( enum_type) )
158+ } else {
159+ errors. push ( Diagnostic {
160+ level : Level :: Error ,
161+ message : format ! ( "Unknown variant '{value}' for enum '{enum_name}'" ) ,
162+ code : Some ( "C000" . to_string ( ) ) ,
163+ spans : vec ! [ SpanLabel {
164+ span,
165+ style: SpanStyle :: Primary ,
166+ label: Some ( "unknown enum variant" . to_string( ) ) ,
167+ } ] ,
168+ } ) ;
169+ Some ( Type :: Error )
170+ }
171+ }
0 commit comments