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