cairo_lang_semantic/expr/
pattern.rs

1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_defs::ids::FunctionWithBodyId;
3use cairo_lang_diagnostics::DiagnosticAdded;
4use cairo_lang_filesystem::ids::SmolStrId;
5use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
6use cairo_lang_syntax::node::ast;
7use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
8use salsa::Database;
9
10use super::fmt::ExprFormatter;
11use crate::items::function_with_body::FunctionWithBodySemantic;
12use crate::{
13    ConcreteStructId, ExprLiteral, ExprStringLiteral, LocalVariable, PatternArena, PatternId,
14    semantic,
15};
16
17/// Semantic representation of a Pattern.
18///
19/// A pattern is a way to "destructure" values. A pattern may introduce new variables that are bound
20/// to inner values of a specific value. For example, a tuple pattern destructures a tuple
21/// and may result in new variables for an elements of that tuple.
22/// This is used both in let statements and match statements.
23// TODO(spapini): Replace this doc with a reference to the language documentation about patterns,
24// once it is available.
25#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject)]
26#[debug_db(ExprFormatter<'db>)]
27pub enum Pattern<'db> {
28    Literal(PatternLiteral<'db>),
29    StringLiteral(PatternStringLiteral<'db>),
30    Variable(PatternVariable<'db>),
31    Struct(PatternStruct<'db>),
32    Tuple(PatternTuple<'db>),
33    FixedSizeArray(PatternFixedSizeArray<'db>),
34    EnumVariant(PatternEnumVariant<'db>),
35    Otherwise(PatternOtherwise<'db>),
36    Missing(PatternMissing<'db>),
37}
38
39impl<'db> Pattern<'db> {
40    pub fn ty(&self) -> semantic::TypeId<'db> {
41        match self {
42            Pattern::Literal(literal) => literal.literal.ty,
43            Pattern::StringLiteral(string_literal) => string_literal.string_literal.ty,
44            Pattern::Variable(variable) => variable.var.ty,
45            Pattern::Struct(pattern_struct) => pattern_struct.ty,
46            Pattern::Tuple(pattern_tuple) => pattern_tuple.ty,
47            Pattern::FixedSizeArray(pattern_fixed_size_array) => pattern_fixed_size_array.ty,
48            Pattern::EnumVariant(pattern_enum_variant) => pattern_enum_variant.ty,
49            Pattern::Otherwise(pattern_otherwise) => pattern_otherwise.ty,
50            Pattern::Missing(pattern_missing) => pattern_missing.ty,
51        }
52    }
53
54    pub fn variables(
55        &self,
56        queryable: &dyn PatternVariablesQueryable<'db>,
57    ) -> Vec<PatternVariable<'db>> {
58        match self {
59            Pattern::Variable(variable) => vec![variable.clone()],
60            Pattern::Struct(pattern_struct) => pattern_struct
61                .field_patterns
62                .iter()
63                .flat_map(|(pattern, _member)| queryable.query(*pattern))
64                .collect(),
65            Pattern::Tuple(pattern_tuple) => pattern_tuple
66                .field_patterns
67                .iter()
68                .flat_map(|pattern| queryable.query(*pattern))
69                .collect(),
70            Pattern::FixedSizeArray(pattern_fixed_size_array) => pattern_fixed_size_array
71                .elements_patterns
72                .iter()
73                .flat_map(|pattern| queryable.query(*pattern))
74                .collect(),
75            Pattern::EnumVariant(pattern_enum_variant) => {
76                match &pattern_enum_variant.inner_pattern {
77                    Some(pattern) => queryable.query(*pattern),
78                    None => vec![],
79                }
80            }
81            Pattern::Literal(_)
82            | Pattern::StringLiteral(_)
83            | Pattern::Otherwise(_)
84            | Pattern::Missing(_) => vec![],
85        }
86    }
87
88    pub fn stable_ptr(&self) -> ast::PatternPtr<'db> {
89        match self {
90            Pattern::Literal(pattern) => pattern.stable_ptr,
91            Pattern::StringLiteral(pattern) => pattern.stable_ptr,
92            Pattern::Variable(pattern) => pattern.stable_ptr,
93            Pattern::Struct(pattern) => pattern.stable_ptr.into(),
94            Pattern::Tuple(pattern) => pattern.stable_ptr.into(),
95            Pattern::FixedSizeArray(pattern) => pattern.stable_ptr.into(),
96            Pattern::EnumVariant(pattern) => pattern.stable_ptr,
97            Pattern::Otherwise(pattern) => pattern.stable_ptr.into(),
98            Pattern::Missing(pattern) => pattern.stable_ptr,
99        }
100    }
101}
102
103impl<'db> From<&Pattern<'db>> for SyntaxStablePtrId<'db> {
104    fn from(pattern: &Pattern<'db>) -> Self {
105        pattern.stable_ptr().into()
106    }
107}
108
109/// Polymorphic container of [`Pattern`] objects used for querying pattern variables.
110pub trait PatternVariablesQueryable<'a> {
111    /// Lookup the pattern in this container and then get [`Pattern::variables`] from it.
112    fn query(&self, id: PatternId) -> Vec<PatternVariable<'a>>;
113}
114
115impl<'a> PatternVariablesQueryable<'a> for PatternArena<'a> {
116    fn query(&self, id: PatternId) -> Vec<PatternVariable<'a>> {
117        self[id].variables(self)
118    }
119}
120
121/// Query a function for variables of patterns defined within it.
122///
123/// This is a wrapper over [`Database`] that takes [`FunctionWithBodyId`]
124/// and relays queries to [`FunctionWithBodySemantic::pattern_semantic`].
125pub struct QueryPatternVariablesFromDb<'a>(
126    pub &'a (dyn Database + 'static),
127    pub FunctionWithBodyId<'a>,
128);
129
130impl<'a> PatternVariablesQueryable<'a> for QueryPatternVariablesFromDb<'a> {
131    fn query(&self, id: PatternId) -> Vec<PatternVariable<'a>> {
132        let pattern: Pattern<'a> = self.0.pattern_semantic(self.1, id);
133        pattern.variables(self)
134    }
135}
136
137#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
138#[debug_db(ExprFormatter<'db>)]
139pub struct PatternLiteral<'db> {
140    pub literal: ExprLiteral<'db>,
141    #[hide_field_debug_with_db]
142    #[dont_rewrite]
143    pub stable_ptr: ast::PatternPtr<'db>,
144}
145
146#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
147#[debug_db(ExprFormatter<'db>)]
148pub struct PatternStringLiteral<'db> {
149    pub string_literal: ExprStringLiteral<'db>,
150    #[hide_field_debug_with_db]
151    #[dont_rewrite]
152    pub stable_ptr: ast::PatternPtr<'db>,
153}
154
155/// A pattern that binds the matched value to a variable.
156#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
157pub struct PatternVariable<'db> {
158    #[dont_rewrite]
159    pub name: SmolStrId<'db>,
160    pub var: LocalVariable<'db>,
161    #[dont_rewrite]
162    pub stable_ptr: ast::PatternPtr<'db>,
163}
164impl<'db> DebugWithDb<'db> for PatternVariable<'db> {
165    type Db = ExprFormatter<'db>;
166
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db ExprFormatter<'_>) -> std::fmt::Result {
168        write!(f, "{}", self.name.long(db.db))
169    }
170}
171
172/// A pattern that destructures a struct to its fields.
173#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject)]
174#[debug_db(ExprFormatter<'db>)]
175pub struct PatternStruct<'db> {
176    pub concrete_struct_id: ConcreteStructId<'db>,
177    // TODO(spapini): This should be ConcreteMember, when available.
178    pub field_patterns: Vec<(PatternId, semantic::Member<'db>)>,
179    pub ty: semantic::TypeId<'db>,
180    #[dont_rewrite]
181    pub n_snapshots: usize,
182    #[hide_field_debug_with_db]
183    #[dont_rewrite]
184    pub stable_ptr: ast::PatternStructPtr<'db>,
185}
186
187/// A pattern that destructures a tuple to its fields.
188#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
189#[debug_db(ExprFormatter<'db>)]
190pub struct PatternTuple<'db> {
191    pub field_patterns: Vec<PatternId>,
192    pub ty: semantic::TypeId<'db>,
193    #[hide_field_debug_with_db]
194    #[dont_rewrite]
195    pub stable_ptr: ast::PatternTuplePtr<'db>,
196}
197
198/// A pattern that destructures a fixed size array into its elements.
199#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
200#[debug_db(ExprFormatter<'db>)]
201pub struct PatternFixedSizeArray<'db> {
202    pub elements_patterns: Vec<PatternId>,
203    pub ty: semantic::TypeId<'db>,
204    #[hide_field_debug_with_db]
205    #[dont_rewrite]
206    pub stable_ptr: ast::PatternFixedSizeArrayPtr<'db>,
207}
208
209/// A pattern that destructures a specific variant of an enum to its inner value.
210#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
211#[debug_db(ExprFormatter<'db>)]
212pub struct PatternEnumVariant<'db> {
213    pub variant: semantic::ConcreteVariant<'db>,
214    pub inner_pattern: Option<PatternId>,
215    pub ty: semantic::TypeId<'db>,
216    #[hide_field_debug_with_db]
217    #[dont_rewrite]
218    pub stable_ptr: ast::PatternPtr<'db>,
219}
220
221#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
222#[debug_db(ExprFormatter<'db>)]
223pub struct PatternOtherwise<'db> {
224    pub ty: semantic::TypeId<'db>,
225    #[hide_field_debug_with_db]
226    #[dont_rewrite]
227    pub stable_ptr: ast::TerminalUnderscorePtr<'db>,
228}
229
230#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
231#[debug_db(ExprFormatter<'db>)]
232pub struct PatternMissing<'db> {
233    pub ty: semantic::TypeId<'db>,
234    #[hide_field_debug_with_db]
235    #[dont_rewrite]
236    pub stable_ptr: ast::PatternPtr<'db>,
237    #[hide_field_debug_with_db]
238    #[dont_rewrite]
239    pub diag_added: DiagnosticAdded,
240}