swamp_analyzer/
call.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::TypeContext;
6use crate::{Analyzer, LocationSide};
7use source_map_node::Node;
8use swamp_semantic::err::ErrorKind;
9use swamp_semantic::{ArgumentExpression, Expression, ExpressionKind};
10use swamp_types::prelude::*;
11
12pub struct MaybeBorrowMutRefExpression {
13    pub ast_expression: swamp_ast::Expression,
14    pub has_borrow_mutable_reference: Option<Node>,
15}
16
17impl Analyzer<'_> {
18    /*
19    TODO: @ideas
20    The analyzer should be more explicit in how the values are transferred, to make it easier and more consistent for the code generator.
21
22    - PassSimpleValue (For simple (primitive) type parameters, not mut, e.g. `a: Int`)
23    - PassSimpleValueWithCopyback (For **mut** simple (primitive) parameters, e.g. `mut a: Int`)
24    - PassComplexAddressFromLValue (For complex type parameters, argument is LValue. can be used for both `mut` and not mut, but is mandatory if is mut)
25    - PassComplexAddressFromRValueMaterialization (For complex type parameters, argument is RValue. Param must NOT be mut)
26
27    */
28    /// # Errors
29    ///
30    pub fn analyze_argument(
31        &mut self,
32        fn_parameter: &TypeForParameter,
33        argument_expr: &swamp_ast::Expression,
34    ) -> ArgumentExpression {
35        let context = TypeContext::new_argument(
36            &fn_parameter.resolved_type,
37            false, // Function arguments cannot provide storage for aggregate return values
38        );
39
40        let ref_checked_argument = self.analyze_maybe_ref_expression(argument_expr);
41
42        if fn_parameter.is_mutable {
43            if ref_checked_argument.has_borrow_mutable_reference.is_none() {
44                // if the parameter is mutable you must pass in mutable reference to it
45                let expr = self.create_err(ErrorKind::ArgumentIsNotMutable, &argument_expr.node);
46                return ArgumentExpression::Expression(expr);
47            }
48            let mut_location = self.analyze_to_location(
49                &ref_checked_argument.ast_expression,
50                &context,
51                LocationSide::Rhs,
52            );
53
54            ArgumentExpression::BorrowMutableReference(mut_location)
55        } else {
56            if ref_checked_argument.has_borrow_mutable_reference.is_some() {
57                {
58                    let expr =
59                        self.create_err(ErrorKind::ParameterIsNotMutable, &argument_expr.node);
60                    // Why did you pass in a mutable reference to something that can not do anything useful with it
61                    return ArgumentExpression::Expression(expr);
62                }
63            }
64
65            let resolved_expr = self.analyze_expression(&ref_checked_argument.ast_expression, &context.with_ephemeral());
66            // Check if this expression needs materialization for fixed-size types
67            if self.needs_materialization(&resolved_expr) {
68                // and then check if it will be possible to create temporary storage:
69                if resolved_expr.ty.is_blittable() {
70                    ArgumentExpression::MaterializedExpression(resolved_expr)
71                } else {
72                    // Error
73                    ArgumentExpression::Expression(self.create_err(ErrorKind::CanNotCreateTemporaryStorage, &argument_expr.node))
74                }
75            } else {
76                ArgumentExpression::Expression(resolved_expr)
77            }
78        }
79    }
80
81    /// # Errors
82    ///
83    pub fn analyze_and_verify_parameters(
84        &mut self,
85        node: &swamp_ast::Node,
86        fn_parameters: &[TypeForParameter],
87        arguments: &[swamp_ast::Expression],
88    ) -> Vec<ArgumentExpression> {
89        if fn_parameters.len() != arguments.len() {
90            self.add_err(
91                ErrorKind::WrongNumberOfArguments(fn_parameters.len(), arguments.len()),
92                node,
93            );
94            return vec![];
95        }
96
97        if fn_parameters.len() > Self::MAX_PARAMETER_COUNT {
98            self.add_err(
99                ErrorKind::TooManyParameters {
100                    encountered: fn_parameters.len(),
101                    allowed: Self::MAX_PARAMETER_COUNT,
102                },
103                node,
104            );
105            return vec![];
106        }
107
108        let mut resolved_arguments = Vec::new();
109        for (fn_parameter, argument_expr) in fn_parameters.iter().zip(arguments) {
110            let mut_or_immutable = self.analyze_argument(fn_parameter, argument_expr);
111            resolved_arguments.push(mut_or_immutable);
112        }
113
114        resolved_arguments
115    }
116
117    /// # Errors
118    ///
119    pub fn analyze_mut_or_immutable_expression(
120        &mut self,
121        expr: &swamp_ast::Expression,
122        context: &TypeContext,
123        location_side: LocationSide,
124    ) -> ArgumentExpression {
125        let maybe_borrow_or_normal_expression = self.analyze_maybe_ref_expression(expr);
126
127        if maybe_borrow_or_normal_expression
128            .has_borrow_mutable_reference
129            .is_some()
130        {
131            ArgumentExpression::BorrowMutableReference(self.analyze_to_location(
132                &maybe_borrow_or_normal_expression.ast_expression,
133                context,
134                location_side,
135            ))
136        } else {
137            ArgumentExpression::Expression(
138                self.analyze_expression(&maybe_borrow_or_normal_expression.ast_expression, context),
139            )
140        }
141    }
142
143    /// Determines if an expression needs materialization (temporary storage)
144    /// This applies to expressions that produce values but don't have a direct memory location
145    /// Only applies to non-mutable parameters with blittable (fixed-size) types
146    /*
147        pub(crate) fn rvalue_needs_memory_location_to_materialize_in(
148        layout_cache: &mut LayoutCache,
149        expr: &Expression,
150    ) -> bool {
151        let specific_kind_of_expression_needs_memory_target = match &expr.kind {
152            // TODO: Should have more robust check here. maybe check primitives instead and invert?
153            ExpressionKind::EnumVariantLiteral(_, _)
154            | ExpressionKind::TupleLiteral(_)
155            | ExpressionKind::InitializerList(_, _)
156            | ExpressionKind::InitializerPairList(_, _) => true,
157            ExpressionKind::Option(_)
158            | ExpressionKind::AnonymousStructLiteral(_)
159            | ExpressionKind::CoerceToAny(_) => true,
160            _ => false,
161        };
162
163        if specific_kind_of_expression_needs_memory_target {
164            true
165        } else {
166            // Easy to forget that you should also check if it's a function call with a return type requiring memory allocation
167            match &expr.kind {
168                ExpressionKind::InternalCall(_, _)
169                | ExpressionKind::HostCall(_, _)
170                | ExpressionKind::IntrinsicCallEx(_, _) => {
171                    let basic_type = layout_cache.layout(&expr.ty);
172                    basic_type.is_aggregate()
173                }
174                _ => false,
175            }
176        }
177    }
178     */
179    const fn needs_materialization(&self, expr: &Expression) -> bool {
180        match &expr.kind {
181            ExpressionKind::ConstantAccess(_) => false,
182            ExpressionKind::VariableAccess(_) => false,
183            ExpressionKind::BinaryOp(_) => true,
184            ExpressionKind::UnaryOp(_) => true,
185            ExpressionKind::PostfixChain(_, _) => false,
186            ExpressionKind::CoerceOptionToBool(_) => false,
187            ExpressionKind::CoerceIntToChar(_) => false,
188            ExpressionKind::CoerceIntToByte(_) => false,
189            ExpressionKind::CoerceToAny(_) => true,
190            ExpressionKind::IntrinsicCallEx(_, _) => true,
191            ExpressionKind::InternalCall(_, _) => true,
192            ExpressionKind::HostCall(_, _) => true,
193            ExpressionKind::VariableDefinition(_, _) => true,
194            ExpressionKind::VariableDefinitionLValue(_, _) => true,
195            ExpressionKind::VariableReassignment(_, _) => true,
196            ExpressionKind::Assignment(_, _) => true,
197            ExpressionKind::CompoundAssignment(_, _, _) => true,
198            ExpressionKind::AnonymousStructLiteral(_) => true,
199            ExpressionKind::NamedStructLiteral(_) => true,
200            ExpressionKind::FloatLiteral(_) => true,
201            ExpressionKind::NoneLiteral => true,
202            ExpressionKind::IntLiteral(_) => true,
203            ExpressionKind::ByteLiteral(_) => true,
204            ExpressionKind::StringLiteral(_) => true,
205            ExpressionKind::BoolLiteral(_) => true,
206            ExpressionKind::EnumVariantLiteral(_, _) => true,
207            ExpressionKind::TupleLiteral(_) => true,
208            ExpressionKind::InitializerList(_, _) => true,
209            ExpressionKind::InitializerPairList(_, _) => true,
210            ExpressionKind::Option(_) => true,
211            ExpressionKind::ForLoop(_, _, _) => true,
212            ExpressionKind::WhileLoop(_, _) => true,
213            ExpressionKind::Block(_) => true,
214            ExpressionKind::Match(_) => true,
215            ExpressionKind::Guard(_) => true,
216            ExpressionKind::If(_, _, _) => true,
217            ExpressionKind::When(_, _, _) => true,
218            ExpressionKind::TupleDestructuring(_, _, _) => true,
219            ExpressionKind::Lambda(_, _) => false,
220            ExpressionKind::BorrowMutRef(_) => true,
221            ExpressionKind::Error(_) => true,
222        }
223    }
224}