1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use super::*;
use crate::{diagnostics::DiagnosticsHandler, AttributeSet, Signature};

/// Represents the declaration of a function in a [Module]
#[derive(Spanned)]
pub struct FunctionDeclaration {
    #[span]
    pub span: SourceSpan,
    pub attrs: AttributeSet,
    pub name: Ident,
    pub signature: Signature,
    pub blocks: Vec<Block>,
}
impl FunctionDeclaration {
    pub fn new(
        span: SourceSpan,
        name: Ident,
        signature: Signature,
        blocks: Vec<Block>,
        attrs: AttributeSet,
    ) -> Self {
        Self {
            span,
            attrs,
            name,
            signature,
            blocks,
        }
    }

    /// Returns true if the entry block and signature match for this declaration
    pub fn is_declaration_valid(&self, diagnostics: &DiagnosticsHandler) -> bool {
        let entry_block = &self.blocks[0];
        if entry_block.params.len() != self.signature.arity() {
            let num_expected = entry_block.params.len();
            let num_declared = self.signature.arity();
            diagnostics
                .diagnostic(Severity::Error)
                .with_message("invalid function")
                .with_primary_label(
                    entry_block.span,
                    "the parameter list of the entry block does not match the function signature",
                )
                .with_secondary_label(
                    self.span,
                    format!("expected {num_expected} parameters, but got {num_declared}"),
                )
                .emit();
            false
        } else {
            let mut is_valid = true;
            for (expected, declared) in self.signature.params.iter().zip(entry_block.params.iter())
            {
                let expected_ty = &expected.ty;
                let declared_ty = &declared.ty;
                if expected_ty != declared_ty {
                    diagnostics
                        .diagnostic(Severity::Error)
                        .with_message("invalid function")
                        .with_primary_label(
                            entry_block.span,
                            "the parameter list of the entry block does not match the function \
                             signature",
                        )
                        .with_secondary_label(
                            declared.span,
                            format!(
                                "expected a parameter of type {expected_ty}, but got {declared_ty}"
                            ),
                        )
                        .emit();
                    is_valid = false;
                }
            }

            is_valid
        }
    }

    pub(super) fn populate_block_map(
        &mut self,
        diagnostics: &DiagnosticsHandler,
    ) -> Result<BlocksById, BlocksById> {
        use alloc::collections::btree_map::Entry;

        let mut blocks_by_id = BlocksById::default();
        let mut is_valid = true;
        for block in core::mem::take(&mut self.blocks).into_iter() {
            match blocks_by_id.entry(block.id) {
                Entry::Vacant(entry) => {
                    entry.insert(block);
                }
                Entry::Occupied(entry) => {
                    diagnostics
                        .diagnostic(Severity::Error)
                        .with_message("invalid block")
                        .with_primary_label(
                            block.span,
                            "a block with the same name has already been declared",
                        )
                        .with_secondary_label(entry.get().span, "previously declared here")
                        .emit();
                    is_valid = false;
                    continue;
                }
            }
        }

        if is_valid {
            Ok(blocks_by_id)
        } else {
            Err(blocks_by_id)
        }
    }
}
impl fmt::Debug for FunctionDeclaration {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("FunctionDeclaration")
            .field("name", &self.name.as_symbol())
            .field("signature", &self.signature)
            .field("blocks", &self.blocks)
            .finish()
    }
}
impl PartialEq for FunctionDeclaration {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name && self.signature == other.signature && self.blocks == other.blocks
    }
}