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 : 13431 : PatternDeclaration::go (AST::Pattern &pattern, Rib::ItemType type)
27 : : {
28 : 13431 : std::vector<PatternBinding> bindings
29 : 26862 : = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
30 : 13431 : PatternDeclaration::go (pattern, type, bindings);
31 : 13431 : }
32 : :
33 : : void
34 : 17512 : PatternDeclaration::go (AST::Pattern &pattern, Rib::ItemType type,
35 : : std::vector<PatternBinding> &bindings)
36 : : {
37 : 17512 : PatternDeclaration resolver (bindings, type);
38 : 17512 : pattern.accept_vis (resolver);
39 : :
40 : 17526 : 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 : 17519 : 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 : 17512 : }
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 : 17232 : pattern.get_locus ()));
69 : 17232 : }
70 : :
71 : : void
72 : 43 : PatternDeclaration::visit (AST::GroupedPattern &pattern)
73 : : {
74 : 43 : pattern.get_pattern_in_parens ().accept_vis (*this);
75 : 43 : }
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 : 128 : PatternDeclaration::visit (AST::PathInExpression &pattern)
85 : : {
86 : 128 : ResolvePath::go (pattern);
87 : 128 : }
88 : :
89 : : void
90 : 191 : PatternDeclaration::visit (AST::TupleStructPattern &pattern)
91 : : {
92 : 191 : ResolvePath::go (pattern.get_path ());
93 : :
94 : 191 : AST::TupleStructItems &items = pattern.get_items ();
95 : 191 : switch (items.get_item_type ())
96 : : {
97 : 0 : case AST::TupleStructItems::RANGE: {
98 : : // TODO
99 : 0 : rust_unreachable ();
100 : : }
101 : 191 : break;
102 : :
103 : 191 : case AST::TupleStructItems::NO_RANGE: {
104 : 191 : auto &items_no_range
105 : : = static_cast<AST::TupleStructItemsNoRange &> (items);
106 : :
107 : 432 : for (auto &inner_pattern : items_no_range.get_patterns ())
108 : : {
109 : 241 : inner_pattern->accept_vis (*this);
110 : : }
111 : : }
112 : : break;
113 : : }
114 : 191 : }
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 : auto &ident = static_cast<AST::StructPatternFieldIdent &> (*field);
144 : :
145 : 63 : Mutability mut
146 : 63 : = ident.is_mut () ? Mutability::Mut : Mutability::Imm;
147 : :
148 : 63 : add_new_binding (ident.get_identifier (), ident.get_node_id (),
149 : 63 : BindingTypeInfo (mut, ident.is_ref (),
150 : 63 : ident.get_locus ()));
151 : : }
152 : 63 : break;
153 : : }
154 : : }
155 : 35 : }
156 : :
157 : : void
158 : 164 : PatternDeclaration::visit (AST::TuplePattern &pattern)
159 : : {
160 : 164 : auto &items = pattern.get_items ();
161 : 164 : switch (items.get_pattern_type ())
162 : : {
163 : 164 : case AST::TuplePatternItems::TuplePatternItemType::MULTIPLE: {
164 : 164 : auto &ref = static_cast<AST::TuplePatternItemsMultiple &> (
165 : 164 : pattern.get_items ());
166 : :
167 : 509 : for (auto &p : ref.get_patterns ())
168 : 345 : p->accept_vis (*this);
169 : : }
170 : : break;
171 : :
172 : 0 : case AST::TuplePatternItems::TuplePatternItemType::RANGED: {
173 : 0 : auto &ref
174 : 0 : = static_cast<AST::TuplePatternItemsRanged &> (pattern.get_items ());
175 : :
176 : 0 : for (auto &p : ref.get_lower_patterns ())
177 : 0 : p->accept_vis (*this);
178 : 0 : for (auto &p : ref.get_upper_patterns ())
179 : 0 : p->accept_vis (*this);
180 : : }
181 : : break;
182 : : }
183 : 164 : }
184 : :
185 : : void
186 : 45 : PatternDeclaration::visit (AST::AltPattern &pattern)
187 : : {
188 : : // push a new set of 'Or' bindings to the stack. Accounts for the
189 : : // alternatives. e.g. in `p_0 | p_1`, bindings to the same identifier between
190 : : // p_0 and p_1 shouldn't cause an error.
191 : 45 : bindings_with_ctx.push_back (
192 : 90 : PatternBinding (PatternBoundCtx::Or, std::set<Identifier> ()));
193 : :
194 : : // This is a hack to avoid creating a separate visitor class for the
195 : : // consistency checks. We empty out the binding_info_map before each iteration
196 : : // to separate between the alts' binding_maps. And right after the alt
197 : : // visit...
198 : 45 : auto tmp_binding_map = binding_info_map;
199 : 45 : binding_info_map.clear ();
200 : :
201 : 45 : std::vector<BindingMap> alts_binding_maps;
202 : :
203 : 150 : for (auto &alt : pattern.get_alts ())
204 : : {
205 : : // before this visit, the binding_info_map is guaranteed to be empty
206 : 105 : rust_assert (binding_info_map.empty ());
207 : :
208 : : // push a new `Product` context to correctly reject multiple bindings
209 : : // within this single alt.
210 : 105 : bindings_with_ctx.push_back (
211 : 210 : PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ()));
212 : :
213 : 105 : alt->accept_vis (*this);
214 : :
215 : : // ...the binding_info_map is (potentially) populated. We copy it to the
216 : : // vector, and empty it out to be ready for the next iteration. And after
217 : : // all the iterations are finished...
218 : 105 : alts_binding_maps.push_back (binding_info_map);
219 : 105 : binding_info_map.clear ();
220 : :
221 : : // Remove the last (i.e. `Product`) context and add the bindings from the
222 : : // visited alt to the one before last (i.e. `Or`). Now (after checking
223 : : // with the alt internally), the bindings from this alt will reside in the
224 : : // `Or` context.
225 : 105 : auto last_bound_idents = bindings_with_ctx.back ().idents;
226 : 105 : bindings_with_ctx.pop_back ();
227 : :
228 : 149 : for (auto &ident : last_bound_idents)
229 : : {
230 : 44 : bindings_with_ctx.back ().idents.insert (ident);
231 : : }
232 : 105 : }
233 : :
234 : : // Now we can finally check for consistency.
235 : 45 : check_bindings_consistency (alts_binding_maps);
236 : :
237 : : // Now we remove the `Or` context we pushed earlier.
238 : : // e.g. in `(a, (p_0 | p_1), c)`: after finishing up inside the alt pattern,
239 : : // we return to the tuple (`Product`) context and push the new bindings.
240 : 45 : auto idents = bindings_with_ctx.back ().idents;
241 : 45 : bindings_with_ctx.pop_back ();
242 : 89 : for (auto &ident : idents)
243 : 44 : bindings_with_ctx.back ().idents.insert (ident.as_string ());
244 : :
245 : : // ...we repopulate the binding_info_map correctly (the initial bindings
246 : : // stored in the tmp_binding_map + all the bindings from all the alts)
247 : 45 : binding_info_map = tmp_binding_map;
248 : 150 : for (auto &alt_map : alts_binding_maps)
249 : 228 : for (auto &map_entry : alt_map)
250 : 123 : binding_info_map.insert (map_entry);
251 : 45 : }
252 : :
253 : : void
254 : 17295 : PatternDeclaration::add_new_binding (Identifier ident, NodeId node_id,
255 : : BindingTypeInfo info)
256 : : {
257 : 17295 : bool has_binding_ctx = bindings_with_ctx.size () > 0;
258 : 17295 : rust_assert (has_binding_ctx);
259 : :
260 : : bool identifier_or_bound = false, identifier_product_bound = false;
261 : :
262 : 34850 : for (auto binding : bindings_with_ctx)
263 : : {
264 : 17555 : bool identifier_bound_here
265 : 17555 : = (binding.idents.find (ident) != binding.idents.end ());
266 : 17555 : if (identifier_bound_here)
267 : : {
268 : 66 : identifier_product_bound |= binding.ctx == PatternBoundCtx::Product;
269 : 66 : identifier_or_bound |= binding.ctx == PatternBoundCtx::Or;
270 : : }
271 : 17555 : }
272 : :
273 : 17295 : if (identifier_product_bound)
274 : : {
275 : 8 : if (type == Rib::ItemType::Param)
276 : : {
277 : 5 : rust_error_at (info.get_locus (), ErrorCode::E0415,
278 : : "identifier '%s' is bound more than once in the "
279 : : "same parameter list",
280 : 5 : ident.as_string ().c_str ());
281 : : }
282 : : else
283 : : {
284 : 3 : rust_error_at (
285 : : info.get_locus (), ErrorCode::E0416,
286 : : "identifier '%s' is bound more than once in the same pattern",
287 : 3 : ident.as_string ().c_str ());
288 : : }
289 : :
290 : 8 : return;
291 : : }
292 : :
293 : 17287 : if (!identifier_or_bound)
294 : : {
295 : 17229 : bindings_with_ctx.back ().idents.insert (ident);
296 : 17229 : resolver->get_name_scope ().insert (
297 : 34458 : CanonicalPath::new_seg (node_id, ident.as_string ()), node_id,
298 : : info.get_locus (), type);
299 : : }
300 : :
301 : 17287 : binding_info_map.insert ({ident, info});
302 : : }
303 : :
304 : : // Verifies that all the alts in an AltPattern have the same set of bindings
305 : : // with the same mutability and reference states.
306 : : void
307 : 45 : PatternDeclaration::check_bindings_consistency (
308 : : std::vector<BindingMap> &binding_maps)
309 : : {
310 : 150 : for (size_t i = 0; i < binding_maps.size (); i++)
311 : : {
312 : 105 : auto &outer_bindings_map = binding_maps[i];
313 : :
314 : 360 : for (size_t j = 0; j < binding_maps.size (); j++)
315 : : {
316 : : // skip comparing the current outer map with itself.
317 : 255 : if (j == i)
318 : 105 : continue;
319 : :
320 : 150 : auto &inner_bindings_map = binding_maps[j];
321 : :
322 : : // iterate over the inner map entries and check if they exist in outer
323 : : // map
324 : 322 : for (auto map_entry : inner_bindings_map)
325 : : {
326 : 172 : auto ident = map_entry.first; // key
327 : 172 : auto inner_info = map_entry.second; // value
328 : 172 : bool ident_is_outer_bound = outer_bindings_map.count (ident);
329 : :
330 : 172 : if (!ident_is_outer_bound && !missing_bindings.count (ident))
331 : 14 : missing_bindings.insert ({ident, inner_info});
332 : :
333 : 158 : else if (outer_bindings_map[ident] != inner_info
334 : 158 : && !inconsistent_bindings.count (ident))
335 : 7 : inconsistent_bindings.insert ({ident, inner_info});
336 : 172 : }
337 : : }
338 : : }
339 : 45 : }
340 : :
341 : : static void
342 : 42 : resolve_range_pattern_bound (AST::RangePatternBound &bound)
343 : : {
344 : 42 : switch (bound.get_bound_type ())
345 : : {
346 : : case AST::RangePatternBound::RangePatternBoundType::LITERAL:
347 : : // Nothing to resolve for a literal.
348 : : break;
349 : :
350 : 21 : case AST::RangePatternBound::RangePatternBoundType::PATH: {
351 : 21 : auto &ref = static_cast<AST::RangePatternBoundPath &> (bound);
352 : :
353 : 21 : ResolvePath::go (ref.get_path ());
354 : : }
355 : 21 : break;
356 : :
357 : 0 : case AST::RangePatternBound::RangePatternBoundType::QUALPATH: {
358 : 0 : auto &ref = static_cast<AST::RangePatternBoundQualPath &> (bound);
359 : :
360 : 0 : ResolvePath::go (ref.get_qualified_path ());
361 : : }
362 : 0 : break;
363 : : }
364 : 42 : }
365 : :
366 : : void
367 : 21 : PatternDeclaration::visit (AST::RangePattern &pattern)
368 : : {
369 : 21 : resolve_range_pattern_bound (pattern.get_upper_bound ());
370 : 21 : resolve_range_pattern_bound (pattern.get_lower_bound ());
371 : 21 : }
372 : :
373 : : void
374 : 0 : PatternDeclaration::visit (AST::SlicePattern &pattern)
375 : : {
376 : 0 : for (auto &p : pattern.get_items ())
377 : : {
378 : 0 : p->accept_vis (*this);
379 : : }
380 : 0 : }
381 : :
382 : : } // namespace Resolver
383 : : } // namespace Rust
|