Branch data Line data Source code
1 : : // Copyright (C) 2020-2024 Free Software Foundation, Inc.
2 : :
3 : : // This file is part of GCC.
4 : :
5 : : // GCC is free software; you can redistribute it and/or modify it under
6 : : // the terms of the GNU General Public License as published by the Free
7 : : // Software Foundation; either version 3, or (at your option) any later
8 : : // version.
9 : :
10 : : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : : // for more details.
14 : :
15 : : // You should have received a copy of the GNU General Public License
16 : : // along with GCC; see the file COPYING3. If not see
17 : : // <http://www.gnu.org/licenses/>.
18 : :
19 : : #include "rust-ast-resolve-pattern.h"
20 : : #include "rust-ast-resolve-path.h"
21 : :
22 : : namespace Rust {
23 : : namespace Resolver {
24 : :
25 : : void
26 : 13435 : PatternDeclaration::go (AST::Pattern *pattern, Rib::ItemType type)
27 : : {
28 : 13435 : std::vector<PatternBinding> bindings
29 : 26870 : = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
30 : 13435 : PatternDeclaration::go (pattern, type, bindings);
31 : 13435 : }
32 : :
33 : : void
34 : 17501 : PatternDeclaration::go (AST::Pattern *pattern, Rib::ItemType type,
35 : : std::vector<PatternBinding> &bindings)
36 : : {
37 : 17501 : PatternDeclaration resolver (bindings, type);
38 : 17501 : pattern->accept_vis (resolver);
39 : :
40 : 17515 : for (auto &map_entry : resolver.missing_bindings)
41 : : {
42 : 14 : auto ident = map_entry.first; // key
43 : 14 : auto info = map_entry.second; // value
44 : :
45 : 14 : rust_error_at (info.get_locus (), ErrorCode::E0408,
46 : : "variable '%s' is not bound in all patterns",
47 : 14 : ident.as_string ().c_str ());
48 : 14 : }
49 : :
50 : 17508 : for (auto &map_entry : resolver.inconsistent_bindings)
51 : : {
52 : 7 : auto ident = map_entry.first; // key
53 : 7 : auto info = map_entry.second; // value
54 : :
55 : 7 : rust_error_at (
56 : : info.get_locus (), ErrorCode::E0409,
57 : : "variable '%s' is bound inconsistently across pattern alternatives",
58 : 7 : ident.as_string ().c_str ());
59 : 7 : }
60 : 17501 : }
61 : :
62 : : void
63 : 17232 : PatternDeclaration::visit (AST::IdentifierPattern &pattern)
64 : : {
65 : 17232 : Mutability mut = pattern.get_is_mut () ? Mutability::Mut : Mutability::Imm;
66 : 17232 : add_new_binding (pattern.get_ident (), pattern.get_node_id (),
67 : 17232 : BindingTypeInfo (mut, pattern.get_is_ref (),
68 : : pattern.get_locus ()));
69 : 17232 : }
70 : :
71 : : void
72 : 42 : PatternDeclaration::visit (AST::GroupedPattern &pattern)
73 : : {
74 : 42 : pattern.get_pattern_in_parens ()->accept_vis (*this);
75 : 42 : }
76 : :
77 : : void
78 : 28 : PatternDeclaration::visit (AST::ReferencePattern &pattern)
79 : : {
80 : 28 : pattern.get_referenced_pattern ()->accept_vis (*this);
81 : 28 : }
82 : :
83 : : void
84 : 124 : PatternDeclaration::visit (AST::PathInExpression &pattern)
85 : : {
86 : 124 : ResolvePath::go (&pattern);
87 : 124 : }
88 : :
89 : : void
90 : 190 : PatternDeclaration::visit (AST::TupleStructPattern &pattern)
91 : : {
92 : 190 : ResolvePath::go (&pattern.get_path ());
93 : :
94 : 190 : std::unique_ptr<AST::TupleStructItems> &items = pattern.get_items ();
95 : 190 : switch (items->get_item_type ())
96 : : {
97 : 0 : case AST::TupleStructItems::RANGE: {
98 : : // TODO
99 : 0 : rust_unreachable ();
100 : : }
101 : 190 : break;
102 : :
103 : 190 : case AST::TupleStructItems::NO_RANGE: {
104 : 190 : AST::TupleStructItemsNoRange &items_no_range
105 : 190 : = static_cast<AST::TupleStructItemsNoRange &> (*items.get ());
106 : :
107 : 431 : for (auto &inner_pattern : items_no_range.get_patterns ())
108 : : {
109 : 241 : inner_pattern.get ()->accept_vis (*this);
110 : : }
111 : : }
112 : : break;
113 : : }
114 : 190 : }
115 : :
116 : : void
117 : 35 : PatternDeclaration::visit (AST::StructPattern &pattern)
118 : : {
119 : 35 : ResolvePath::go (&pattern.get_path ());
120 : :
121 : 35 : auto &struct_pattern_elems = pattern.get_struct_pattern_elems ();
122 : 100 : for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
123 : : {
124 : 65 : switch (field->get_item_type ())
125 : : {
126 : 1 : case AST::StructPatternField::ItemType::TUPLE_PAT: {
127 : 1 : AST::StructPatternFieldTuplePat &tuple
128 : 1 : = static_cast<AST::StructPatternFieldTuplePat &> (*field);
129 : :
130 : 1 : tuple.get_index_pattern ()->accept_vis (*this);
131 : : }
132 : 1 : break;
133 : :
134 : 1 : case AST::StructPatternField::ItemType::IDENT_PAT: {
135 : 1 : AST::StructPatternFieldIdentPat &ident
136 : 1 : = static_cast<AST::StructPatternFieldIdentPat &> (*field);
137 : :
138 : 1 : ident.get_ident_pattern ()->accept_vis (*this);
139 : : }
140 : 1 : break;
141 : :
142 : 63 : case AST::StructPatternField::ItemType::IDENT: {
143 : 63 : AST::StructPatternFieldIdent &ident
144 : 63 : = static_cast<AST::StructPatternFieldIdent &> (*field.get ());
145 : :
146 : 63 : Mutability mut
147 : 63 : = ident.is_mut () ? Mutability::Mut : Mutability::Imm;
148 : :
149 : 63 : add_new_binding (ident.get_identifier (), ident.get_node_id (),
150 : 63 : BindingTypeInfo (mut, ident.is_ref (),
151 : : ident.get_locus ()));
152 : : }
153 : 63 : break;
154 : : }
155 : : }
156 : 35 : }
157 : :
158 : : void
159 : 164 : PatternDeclaration::visit (AST::TuplePattern &pattern)
160 : : {
161 : 164 : std::unique_ptr<AST::TuplePatternItems> &items = pattern.get_items ();
162 : 164 : switch (items->get_pattern_type ())
163 : : {
164 : 164 : case AST::TuplePatternItems::TuplePatternItemType::MULTIPLE: {
165 : 164 : AST::TuplePatternItemsMultiple &ref
166 : : = *static_cast<AST::TuplePatternItemsMultiple *> (
167 : 164 : pattern.get_items ().get ());
168 : :
169 : 509 : for (auto &p : ref.get_patterns ())
170 : 345 : p->accept_vis (*this);
171 : : }
172 : : break;
173 : :
174 : 0 : case AST::TuplePatternItems::TuplePatternItemType::RANGED: {
175 : 0 : AST::TuplePatternItemsRanged &ref
176 : : = *static_cast<AST::TuplePatternItemsRanged *> (
177 : 0 : pattern.get_items ().get ());
178 : :
179 : 0 : for (auto &p : ref.get_lower_patterns ())
180 : 0 : p->accept_vis (*this);
181 : 0 : for (auto &p : ref.get_upper_patterns ())
182 : 0 : p->accept_vis (*this);
183 : : }
184 : : break;
185 : : }
186 : 164 : }
187 : :
188 : : void
189 : 44 : PatternDeclaration::visit (AST::AltPattern &pattern)
190 : : {
191 : : // push a new set of 'Or' bindings to the stack. Accounts for the
192 : : // alternatives. e.g. in `p_0 | p_1`, bindings to the same identifier between
193 : : // p_0 and p_1 shouldn't cause an error.
194 : 44 : bindings_with_ctx.push_back (
195 : 88 : PatternBinding (PatternBoundCtx::Or, std::set<Identifier> ()));
196 : :
197 : : // This is a hack to avoid creating a separate visitor class for the
198 : : // consistency checks. We empty out the binding_info_map before each iteration
199 : : // to separate between the alts' binding_maps. And right after the alt
200 : : // visit...
201 : 44 : auto tmp_binding_map = binding_info_map;
202 : 44 : binding_info_map.clear ();
203 : :
204 : 44 : std::vector<BindingMap> alts_binding_maps;
205 : :
206 : 147 : for (auto &alt : pattern.get_alts ())
207 : : {
208 : : // before this visit, the binding_info_map is guaranteed to be empty
209 : 103 : rust_assert (binding_info_map.empty ());
210 : :
211 : : // push a new `Product` context to correctly reject multiple bindings
212 : : // within this single alt.
213 : 103 : bindings_with_ctx.push_back (
214 : 206 : PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ()));
215 : :
216 : 103 : alt->accept_vis (*this);
217 : :
218 : : // ...the binding_info_map is (potentially) populated. We copy it to the
219 : : // vector, and empty it out to be ready for the next iteration. And after
220 : : // all the iterations are finished...
221 : 103 : alts_binding_maps.push_back (binding_info_map);
222 : 103 : binding_info_map.clear ();
223 : :
224 : : // Remove the last (i.e. `Product`) context and add the bindings from the
225 : : // visited alt to the one before last (i.e. `Or`). Now (after checking
226 : : // with the alt internally), the bindings from this alt will reside in the
227 : : // `Or` context.
228 : 103 : auto last_bound_idents = bindings_with_ctx.back ().idents;
229 : 103 : bindings_with_ctx.pop_back ();
230 : :
231 : 146 : for (auto &ident : last_bound_idents)
232 : : {
233 : 43 : bindings_with_ctx.back ().idents.insert (ident);
234 : : }
235 : 103 : }
236 : :
237 : : // Now we can finally check for consistency.
238 : 44 : check_bindings_consistency (alts_binding_maps);
239 : :
240 : : // Now we remove the `Or` context we pushed earlier.
241 : : // e.g. in `(a, (p_0 | p_1), c)`: after finishing up inside the alt pattern,
242 : : // we return to the tuple (`Product`) context and push the new bindings.
243 : 44 : auto idents = bindings_with_ctx.back ().idents;
244 : 44 : bindings_with_ctx.pop_back ();
245 : 87 : for (auto &ident : idents)
246 : 43 : bindings_with_ctx.back ().idents.insert (ident.as_string ());
247 : :
248 : : // ...we repopulate the binding_info_map correctly (the initial bindings
249 : : // stored in the tmp_binding_map + all the bindings from all the alts)
250 : 44 : binding_info_map = tmp_binding_map;
251 : 147 : for (auto &alt_map : alts_binding_maps)
252 : 224 : for (auto &map_entry : alt_map)
253 : 121 : binding_info_map.insert (map_entry);
254 : 44 : }
255 : :
256 : : void
257 : 17295 : PatternDeclaration::add_new_binding (Identifier ident, NodeId node_id,
258 : : BindingTypeInfo info)
259 : : {
260 : 17295 : bool has_binding_ctx = bindings_with_ctx.size () > 0;
261 : 17295 : rust_assert (has_binding_ctx);
262 : :
263 : : bool identifier_or_bound = false, identifier_product_bound = false;
264 : :
265 : 34846 : for (auto binding : bindings_with_ctx)
266 : : {
267 : 17551 : bool identifier_bound_here
268 : 17551 : = (binding.idents.find (ident) != binding.idents.end ());
269 : 17551 : if (identifier_bound_here)
270 : : {
271 : 65 : identifier_product_bound |= binding.ctx == PatternBoundCtx::Product;
272 : 65 : identifier_or_bound |= binding.ctx == PatternBoundCtx::Or;
273 : : }
274 : 17551 : }
275 : :
276 : 17295 : if (identifier_product_bound)
277 : : {
278 : 8 : if (type == Rib::ItemType::Param)
279 : : {
280 : 5 : rust_error_at (info.get_locus (), ErrorCode::E0415,
281 : : "identifier '%s' is bound more than once in the "
282 : : "same parameter list",
283 : 5 : ident.as_string ().c_str ());
284 : : }
285 : : else
286 : : {
287 : 3 : rust_error_at (
288 : : info.get_locus (), ErrorCode::E0416,
289 : : "identifier '%s' is bound more than once in the same pattern",
290 : 3 : ident.as_string ().c_str ());
291 : : }
292 : :
293 : 8 : return;
294 : : }
295 : :
296 : 17287 : if (!identifier_or_bound)
297 : : {
298 : 17230 : bindings_with_ctx.back ().idents.insert (ident);
299 : 17230 : resolver->get_name_scope ().insert (
300 : 34460 : CanonicalPath::new_seg (node_id, ident.as_string ()), node_id,
301 : : info.get_locus (), type);
302 : : }
303 : :
304 : 17287 : binding_info_map.insert ({ident, info});
305 : : }
306 : :
307 : : // Verifies that all the alts in an AltPattern have the same set of bindings
308 : : // with the same mutability and reference states.
309 : : void
310 : 44 : PatternDeclaration::check_bindings_consistency (
311 : : std::vector<BindingMap> &binding_maps)
312 : : {
313 : 147 : for (size_t i = 0; i < binding_maps.size (); i++)
314 : : {
315 : 103 : auto &outer_bindings_map = binding_maps[i];
316 : :
317 : 354 : for (size_t j = 0; j < binding_maps.size (); j++)
318 : : {
319 : : // skip comparing the current outer map with itself.
320 : 251 : if (j == i)
321 : 103 : continue;
322 : :
323 : 148 : auto &inner_bindings_map = binding_maps[j];
324 : :
325 : : // iterate over the inner map entries and check if they exist in outer
326 : : // map
327 : 318 : for (auto map_entry : inner_bindings_map)
328 : : {
329 : 170 : auto ident = map_entry.first; // key
330 : 170 : auto inner_info = map_entry.second; // value
331 : 170 : bool ident_is_outer_bound = outer_bindings_map.count (ident);
332 : :
333 : 170 : if (!ident_is_outer_bound && !missing_bindings.count (ident))
334 : 14 : missing_bindings.insert ({ident, inner_info});
335 : :
336 : 326 : else if (outer_bindings_map[ident] != inner_info
337 : 14 : && !inconsistent_bindings.count (ident))
338 : 7 : inconsistent_bindings.insert ({ident, inner_info});
339 : 170 : }
340 : : }
341 : : }
342 : 44 : }
343 : :
344 : : static void
345 : 42 : resolve_range_pattern_bound (AST::RangePatternBound *bound)
346 : : {
347 : 42 : switch (bound->get_bound_type ())
348 : : {
349 : : case AST::RangePatternBound::RangePatternBoundType::LITERAL:
350 : : // Nothing to resolve for a literal.
351 : : break;
352 : :
353 : 21 : case AST::RangePatternBound::RangePatternBoundType::PATH: {
354 : 21 : AST::RangePatternBoundPath &ref
355 : : = *static_cast<AST::RangePatternBoundPath *> (bound);
356 : :
357 : 21 : ResolvePath::go (&ref.get_path ());
358 : : }
359 : 21 : break;
360 : :
361 : 0 : case AST::RangePatternBound::RangePatternBoundType::QUALPATH: {
362 : 0 : AST::RangePatternBoundQualPath &ref
363 : : = *static_cast<AST::RangePatternBoundQualPath *> (bound);
364 : :
365 : 0 : ResolvePath::go (&ref.get_qualified_path ());
366 : : }
367 : 0 : break;
368 : : }
369 : 42 : }
370 : :
371 : : void
372 : 21 : PatternDeclaration::visit (AST::RangePattern &pattern)
373 : : {
374 : 21 : resolve_range_pattern_bound (pattern.get_upper_bound ().get ());
375 : 21 : resolve_range_pattern_bound (pattern.get_lower_bound ().get ());
376 : 21 : }
377 : :
378 : : void
379 : 0 : PatternDeclaration::visit (AST::SlicePattern &pattern)
380 : : {
381 : 0 : for (auto &p : pattern.get_items ())
382 : : {
383 : 0 : p->accept_vis (*this);
384 : : }
385 : 0 : }
386 : :
387 : : } // namespace Resolver
388 : : } // namespace Rust
|