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}