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(
62            symbol_id,
63            Symbol {
64                id: symbol_id.into(),
65                kind: SymbolKind::Const,
66                source_map_node: name_node.clone(),
67                name: name_node.clone(),
68            },
69        );
70        let constant = Constant {
71            symbol_id,
72            name: name_node.clone(),
73            assigned_name: name_text,
74            id: ConstantId::from(self.shared.state.internal_function_id_allocator.alloc()),
75            expr: resolved_expr,
76            resolved_type: actual_constant_type,
77            function_scope_state: self.scope.total_scopes.clone(),
78        };
79
80        let const_ref = match self.shared.definition_table.add_constant(constant) {
81            Ok(c) => c,
82            Err(sem_err) => {
83                self.add_err_resolved(ErrorKind::SemanticError(sem_err), &name_node);
84                return;
85            }
86        };
87
88        match self
89            .shared
90            .lookup_table
91            .add_constant_link(const_ref.clone())
92        {
93            Ok(c) => c,
94            Err(sem_err) => {
95                self.add_err_resolved(ErrorKind::SemanticError(sem_err), &name_node);
96                return;
97            }
98        }
99
100        // This extra storage of the constants in modules is to have them in analyze / dependency order
101        self.shared
102            .state
103            .constants_in_dependency_order
104            .push(const_ref);
105    }
106
107    pub(crate) fn analyze_constant_definition(&mut self, constant: &swamp_ast::ConstantInfo) {
108        self.analyze_constant(constant);
109    }
110
111    pub(crate) fn analyze_constant_access(
112        &mut self,
113        qualified_constant_identifier: &swamp_ast::QualifiedConstantIdentifier,
114    ) -> Expression {
115        match self
116            .try_find_constant(qualified_constant_identifier)
117            .cloned()
118        {
119            None => self.create_err(
120                ErrorKind::UnknownConstant,
121                &qualified_constant_identifier.name,
122            ),
123            Some(constant_ref) => {
124                let ty = constant_ref.resolved_type.clone();
125
126                let constant_reference_name_node =
127                    self.to_node(&qualified_constant_identifier.name);
128                self.shared.state.refs.add(
129                    constant_ref.symbol_id.into(),
130                    constant_reference_name_node.clone(),
131                );
132                self.shared
133                    .definition_table
134                    .refs
135                    .add(constant_ref.symbol_id.into(), constant_reference_name_node);
136
137                self.create_expr(
138                    ExpressionKind::ConstantAccess(constant_ref),
139                    ty,
140                    &qualified_constant_identifier.name,
141                )
142            }
143        }
144    }
145
146    #[must_use]
147    pub fn try_find_constant(
148        &self,
149        qualified_constant_identifier: &swamp_ast::QualifiedConstantIdentifier,
150    ) -> Option<&ConstantRef> {
151        let path = self.get_module_path(qualified_constant_identifier.module_path.as_ref());
152        let constant_name = self.get_text(&qualified_constant_identifier.name);
153
154        let maybe_symbol_table = self.shared.get_definition_table(&path);
155        maybe_symbol_table.map_or_else(
156            || None,
157            |symbol_table| Some(symbol_table.get_constant(constant_name)),
158        )?
159    }
160}