slang_frontend/semantic_analysis/validation/inference.rs
1use slang_ir::ast::{LetStatement, LiteralValue};
2use slang_shared::CompilationContext;
3use slang_types::{TypeId, TYPE_NAME_U32, TYPE_NAME_U64};
4
5use super::super::{
6 traits::SemanticResult,
7 error::SemanticAnalysisError,
8};
9use super::coercion::{check_unspecified_int_for_type, check_unspecified_float_for_type};
10
11/// Handles type inference and finalization
12///
13/// This module is responsible for inferring types when they are not explicitly
14/// specified and finalizing unspecified literal types to concrete types.
15pub struct TypeInference{
16}
17
18impl TypeInference{
19
20
21 /// Infers the type of a literal expression
22 ///
23 /// # Arguments
24 /// * `literal_value` - The literal value to infer type for
25 ///
26 /// # Returns
27 /// The inferred TypeId for the literal
28 pub fn infer_literal_type(&self, literal_value: &LiteralValue) -> TypeId {
29 match literal_value {
30 LiteralValue::Boolean(_) => TypeId::bool(),
31 LiteralValue::UnspecifiedInteger(_) => TypeId::unspecified_int(),
32 LiteralValue::UnspecifiedFloat(_) => TypeId::unspecified_float(),
33 LiteralValue::I32(_) => TypeId::i32(),
34 LiteralValue::I64(_) => TypeId::i64(),
35 LiteralValue::U32(_) => TypeId::u32(),
36 LiteralValue::U64(_) => TypeId::u64(),
37 LiteralValue::F32(_) => TypeId::f32(),
38 LiteralValue::F64(_) => TypeId::f64(),
39 LiteralValue::String(_) => TypeId::string(),
40 LiteralValue::Unit => TypeId::unit(),
41 }
42 }
43
44 /// Checks if a type requires special handling for unsigned integer assignment
45 ///
46 /// # Arguments
47 /// * `type_id` - The type to check
48 ///
49 /// # Returns
50 /// `true` if the type is unsigned integer, `false` otherwise
51 pub fn is_unsigned_type(&self, type_id: &TypeId) -> bool {
52 type_id == &TypeId::u32() ||
53 type_id == &TypeId::u64()
54 }
55}
56
57/// Converts unspecified literal types to concrete types.
58/// This is used to assign a default concrete type when an unspecified literal
59/// is used in a context where the type wasn't explicitly given.
60///
61/// ### Arguments
62/// * `type_id` - The type to finalize
63///
64/// ### Returns
65/// * The concrete type (i64 for unspecified integers, f64 for unspecified floats)
66/// * The original type if it wasn't an unspecified literal type
67pub fn finalize_inferred_type(type_id: TypeId) -> TypeId {
68 if type_id == TypeId::unspecified_int() {
69 TypeId::i64()
70 } else if type_id == TypeId::unspecified_float() {
71 TypeId::f64()
72 } else {
73 type_id
74 }
75}
76
77/// Determines the final type of a variable in a let statement based on both the
78/// declared type (if any) and the initialization expression's type.
79/// Handles type inference and coercion of unspecified literals.
80///
81/// ### Arguments
82/// * `context` - The compilation context
83/// * `let_stmt` - The let statement being analyzed
84/// * `expr_type` - The type of the initialization expression
85///
86/// ### Returns
87/// * `Ok(type_id)` with the final determined type if valid
88/// * `Err` with a SemanticAnalysisError if there's a type mismatch
89pub fn determine_let_statement_type(
90 context: &CompilationContext,
91 let_stmt: &LetStatement,
92 expr_type: TypeId,
93) -> SemanticResult {
94 // Type inference case - no explicit type annotation
95 if let_stmt.expr_type == TypeId::unknown() {
96 return Ok(expr_type);
97 }
98
99 // Exact type match
100 if let_stmt.expr_type == expr_type {
101 // Special validation for unsigned types to catch negative values early
102 if is_unsigned_type(context, &let_stmt.expr_type) {
103 check_unspecified_int_for_type(context, &let_stmt.value, &let_stmt.expr_type)?;
104 }
105 return Ok(let_stmt.expr_type.clone());
106 }
107
108 // Function type compatibility check
109 if context.get_function_type(&let_stmt.expr_type).is_some() &&
110 context.get_function_type(&expr_type).is_some() {
111 if let_stmt.expr_type == expr_type {
112 return Ok(let_stmt.expr_type.clone());
113 } else {
114 return Err(SemanticAnalysisError::TypeMismatch {
115 expected: let_stmt.expr_type.clone(),
116 actual: expr_type,
117 context: Some(let_stmt.name.clone()),
118 location: let_stmt.location,
119 });
120 }
121 }
122
123 // Handle unspecified integer assignment
124 if expr_type == TypeId::unspecified_int() {
125 return handle_unspecified_int_assignment(context, let_stmt, &expr_type);
126 }
127
128 // Handle unspecified float assignment
129 if expr_type == TypeId::unspecified_float() {
130 return handle_unspecified_float_assignment(context, let_stmt, &expr_type);
131 }
132
133 // No valid coercion possible
134 Err(SemanticAnalysisError::TypeMismatch {
135 expected: let_stmt.expr_type.clone(),
136 actual: expr_type,
137 context: Some(let_stmt.name.clone()),
138 location: let_stmt.location,
139 })
140}
141
142/// Handles assignment of an unspecified integer literal to a variable with a declared type.
143///
144/// ### Arguments
145/// * `context` - The compilation context
146/// * `let_stmt` - The let statement being analyzed.
147/// * `_expr_type` - The type of the initialization expression (should be unspecified_int_type).
148///
149/// ### Returns
150/// * `Ok(type_id)` with the declared type if the literal is valid for that type.
151/// * `Err` with a SemanticAnalysisError if there's a type mismatch or value out of range.
152pub fn handle_unspecified_int_assignment(
153 context: &CompilationContext,
154 let_stmt: &LetStatement,
155 _expr_type: &TypeId,
156) -> SemanticResult {
157 if is_integer_type(context, &let_stmt.expr_type) {
158 check_unspecified_int_for_type(context, &let_stmt.value, &let_stmt.expr_type)
159 } else {
160 Err(SemanticAnalysisError::TypeMismatch {
161 expected: let_stmt.expr_type.clone(),
162 actual: TypeId::unspecified_int(),
163 context: Some(let_stmt.name.clone()),
164 location: let_stmt.location,
165 })
166 }
167}
168
169/// Handles assignment of an unspecified float literal to a variable with a declared type.
170///
171/// ### Arguments
172/// * `context` - The compilation context
173/// * `let_stmt` - The let statement being analyzed.
174/// * `_expr_type` - The type of the initialization expression (should be unspecified_float_type).
175///
176/// ### Returns
177/// * `Ok(type_id)` with the declared type if the literal is valid for that type.
178/// * `Err` with a SemanticAnalysisError if there's a type mismatch or value out of range.
179pub fn handle_unspecified_float_assignment(
180 context: &CompilationContext,
181 let_stmt: &LetStatement,
182 _expr_type: &TypeId,
183) -> SemanticResult {
184 if is_float_type(context, &let_stmt.expr_type) {
185 check_unspecified_float_for_type(context, &let_stmt.value, &let_stmt.expr_type)
186 } else {
187 Err(SemanticAnalysisError::TypeMismatch {
188 expected: let_stmt.expr_type.clone(),
189 actual: TypeId::unspecified_float(),
190 context: Some(let_stmt.name.clone()),
191 location: let_stmt.location,
192 })
193 }
194}
195
196/// Checks if a type is an integer type
197///
198/// ### Arguments
199/// * `context` - The compilation context
200/// * `type_id` - The type ID to check
201///
202/// ### Returns
203/// True if the type is an integer type, false otherwise
204pub fn is_integer_type(context: &CompilationContext, type_id: &TypeId) -> bool {
205 context.is_integer_type(type_id)
206}
207
208/// Checks if a type is a float type
209///
210/// ### Arguments
211/// * `context` - The compilation context
212/// * `type_id` - The type ID to check
213///
214/// ### Returns
215/// True if the type is a float type, false otherwise
216pub fn is_float_type(context: &CompilationContext, type_id: &TypeId) -> bool {
217 context.is_float_type(type_id)
218}
219
220/// Checks if a type is an unsigned integer type
221///
222/// ### Arguments
223/// * `context` - The compilation context
224/// * `type_id` - The type to check
225///
226/// ### Returns
227/// * `true` if the type is u32 or u64, `false` otherwise
228pub fn is_unsigned_type(context: &CompilationContext, type_id: &TypeId) -> bool {
229 let type_name = context.get_type_name(type_id);
230 type_name == TYPE_NAME_U64 || type_name == TYPE_NAME_U32
231}