swamp_analyzer/
constant.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use crate::types::TypeAnalyzeContext;
6use crate::{Analyzer, TypeContext};
7use swamp_semantic::err::ErrorKind;
8use swamp_semantic::{Constant, ConstantId, ConstantRef, Expression, ExpressionKind};
9use swamp_symbol::{Symbol, SymbolKind};
10
11impl Analyzer<'_> {
12    fn analyze_constant(&mut self, constant: &swamp_ast::ConstantInfo) {
13        let maybe_annotation_type = constant.annotation.as_ref().map(|found_ast_type| {
14            self.analyze_type(found_ast_type, &TypeAnalyzeContext::default())
15        });
16
17        let context = TypeContext::new_unsure_argument(maybe_annotation_type.as_ref(), true);
18
19        let resolved_expr = self.analyze_expression(&constant.expression, &context);
20
21        let actual_constant_type = if let Some(annotation_type) = maybe_annotation_type {
22            let extra_verification = false;
23            if extra_verification {
24                let debug_context = TypeContext::new_anything_argument(
25                    annotation_type.collection_view_that_needs_explicit_storage(),
26                );
27                let worked_without_annotation =
28                    self.analyze_expression(&constant.expression, &debug_context);
29                if self
30                    .shared
31                    .state
32                    .types
33                    .compatible_with(&annotation_type, &worked_without_annotation.ty)
34                {
35                    let identifier_name = { self.get_text(&constant.constant_identifier.0) };
36                    eprintln!(
37                        "annotation was not needed for constant: {identifier_name} in {:?}",
38                        self.module_path
39                    );
40                }
41            }
42            annotation_type
43        } else {
44            resolved_expr.ty.clone()
45        };
46
47        // TODO: do not use identifier_name except for asserts
48        if !actual_constant_type.can_be_stored_in_transient_field() {
49            self.add_err(ErrorKind::NeedStorage, &constant.constant_identifier.0);
50            return;
51        }
52        if !actual_constant_type.can_be_stored_in_field() {
53            self.add_hint(ErrorKind::NeedStorage, &constant.constant_identifier.0);
54        }
55
56        // TODO: investigate why FixedSizeArray gets converted to InitializerList
57
58        let name_node = self.to_node(&constant.constant_identifier.0);
59        let name_text = self.get_text_resolved(&name_node).to_string();
60        let symbol_id = self.shared.state.symbol_id_allocator.alloc_top_level();
61        self.shared.state.symbols.insert_top(symbol_id, Symbol {
62            id: symbol_id.into(),
63            kind: SymbolKind::Const,
64            source_map_node: name_node.clone(),
65            name: name_node.clone(),
66        });
67        let constant = Constant {
68            symbol_id,
69            name: name_node.clone(),
70            assigned_name: name_text,
71            id: ConstantId::from(self.shared.state.internal_function_id_allocator.alloc()),
72            expr: resolved_expr,
73            resolved_type: actual_constant_type,
74            function_scope_state: self.scope.total_scopes.clone(),
75        };
76
77        let const_ref = match self.shared.definition_table.add_constant(constant) {
78            Ok(c) => c,
79            Err(sem_err) => {
80                self.add_err_resolved(ErrorKind::SemanticError(sem_err), &name_node);
81                return;
82            }
83        };
84
85        match self
86            .shared
87            .lookup_table
88            .add_constant_link(const_ref.clone())
89        {
90            Ok(c) => c,
91            Err(sem_err) => {
92                self.add_err_resolved(ErrorKind::SemanticError(sem_err), &name_node);
93                return;
94            }
95        }
96
97        // This extra storage of the constants in modules is to have them in analyze / dependency order
98        self.shared
99            .state
100            .constants_in_dependency_order
101            .push(const_ref);
102    }
103
104    pub(crate) fn analyze_constant_definition(&mut self, constant: &swamp_ast::ConstantInfo) {
105        self.analyze_constant(constant);
106    }
107
108    pub(crate) fn analyze_constant_access(
109        &mut self,
110        qualified_constant_identifier: &swamp_ast::QualifiedConstantIdentifier,
111    ) -> Expression {
112        match self.try_find_constant(qualified_constant_identifier).cloned() {
113            None => self.create_err(
114                ErrorKind::UnknownConstant,
115                &qualified_constant_identifier.name,
116            ),
117            Some(constant_ref) => {
118                let ty = constant_ref.resolved_type.clone();
119
120                let constant_reference_name_node = self.to_node(&qualified_constant_identifier.name);
121                self.shared.state.refs.add(constant_ref.symbol_id.into(), constant_reference_name_node.clone());
122                self.shared.definition_table.refs.add(constant_ref.symbol_id.into(), constant_reference_name_node);
123
124                self.create_expr(
125                    ExpressionKind::ConstantAccess(constant_ref),
126                    ty,
127                    &qualified_constant_identifier.name,
128                )
129            }
130        }
131    }
132
133    #[must_use]
134    pub fn try_find_constant(
135        &self,
136        qualified_constant_identifier: &swamp_ast::QualifiedConstantIdentifier,
137    ) -> Option<&ConstantRef> {
138        let path = self.get_module_path(qualified_constant_identifier.module_path.as_ref());
139        let constant_name = self.get_text(&qualified_constant_identifier.name);
140
141        let maybe_symbol_table = self.shared.get_definition_table(&path);
142        maybe_symbol_table.map_or_else(
143            || None,
144            |symbol_table| Some(symbol_table.get_constant(constant_name)),
145        )?
146    }
147}