Line data Source code
1 : // Copyright (C) 2026 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-hir-pattern-abstract.h"
20 : #include "rust-hir-pattern.h"
21 : #include "rust-hir-item.h"
22 : #include "optional.h"
23 : #include "rust-tyty.h"
24 : #include "rust-hir-type-check.h"
25 : namespace Rust {
26 : namespace HIR {
27 :
28 : static bool
29 43 : is_refutable_with_lookup (Pattern &pattern)
30 : {
31 43 : auto context = Resolver::TypeCheckContext::get ();
32 43 : HirId hir_id = pattern.get_mappings ().get_hirid ();
33 43 : if (hir_id)
34 : {
35 43 : TyTy::BaseType *ty = nullptr;
36 43 : if (context->lookup_type (hir_id, &ty))
37 : {
38 43 : if (pattern.is_refutable (*ty))
39 4 : return true;
40 : }
41 : else
42 : {
43 0 : rust_internal_error_at (
44 0 : pattern.get_locus (),
45 : "failed to lookup hir item during refutability checks");
46 : return true;
47 : }
48 : }
49 : return false;
50 : }
51 :
52 : bool
53 2 : PathPattern::is_refutable (const TyTy::BaseType &scrutinee) const
54 : {
55 2 : rust_assert (kind != Kind::LangItem);
56 :
57 2 : auto &mappings = Analysis::Mappings::get ();
58 2 : HirId hir_id = get_final_segment ().get_mappings ().get_hirid ();
59 2 : if (hir_id)
60 : {
61 2 : auto item = mappings.lookup_hir_item (hir_id);
62 2 : if (item && item.value ()->get_item_kind () == Item::ItemKind::Constant)
63 : {
64 0 : return true;
65 : }
66 : else
67 : {
68 2 : rust_internal_error_at (
69 2 : get_locus (),
70 : "failed to lookup hir item during refutability checks");
71 : return true;
72 : }
73 : }
74 :
75 : // A path pattern is irrefutable if it corresponds to an enum with one variant
76 0 : if (scrutinee.get_kind () == TyTy::TypeKind::ADT)
77 : {
78 0 : const auto &adt = static_cast<const TyTy::ADTType &> (scrutinee);
79 0 : if (adt.is_enum ())
80 : {
81 0 : return adt.number_of_variants () > 1;
82 : }
83 : }
84 : // cannot have a Path that is neither a constant or an enum-like ADT
85 0 : rust_unreachable ();
86 : }
87 :
88 : bool
89 7 : SlicePattern::is_refutable (const TyTy::BaseType &scrutinee) const
90 : {
91 : // A slice pattern is refutable if the scrutinee is a dynamic slice,
92 : // and the pattern contains anything other than just a rest pattern
93 7 : if (scrutinee.get_kind () == TyTy::TypeKind::SLICE)
94 1 : if (items->get_item_type () == SlicePatternItems::ItemType::HAS_REST)
95 : {
96 1 : const auto &items_has_rest
97 1 : = static_cast<const SlicePatternItemsHasRest &> (*items);
98 1 : if (items_has_rest.get_lower_patterns ().empty ()
99 1 : && items_has_rest.get_upper_patterns ().empty ())
100 : return false;
101 : }
102 :
103 6 : rust_assert (scrutinee.get_kind () == TyTy::TypeKind::ARRAY);
104 6 : const auto &arr = static_cast<const TyTy::ArrayType &> (scrutinee);
105 6 : switch (items->get_item_type ())
106 : {
107 2 : case SlicePatternItems::ItemType::NO_REST:
108 2 : {
109 2 : const auto &items_no_rest
110 2 : = static_cast<const SlicePatternItemsNoRest &> (*items);
111 2 : const auto *capacity_ty = arr.get_capacity ();
112 2 : rust_assert (capacity_ty->get_kind () == TyTy::TypeKind::CONST);
113 2 : auto *capacity_const = capacity_ty->as_const_type ();
114 2 : rust_assert (capacity_const->const_kind ()
115 : == TyTy::BaseConstType::ConstKind::Value);
116 2 : auto &capacity_value
117 : = *static_cast<const TyTy::ConstValueType *> (capacity_const);
118 2 : auto cap_tree = capacity_value.get_value ();
119 2 : rust_assert (!error_operand_p (cap_tree));
120 2 : size_t cap = (size_t) wi::to_wide (cap_tree).to_uhwi ();
121 :
122 2 : if (items_no_rest.get_patterns ().size () != cap)
123 : return true;
124 8 : for (const auto &pattern : items_no_rest.get_patterns ())
125 6 : if (is_refutable_with_lookup (*pattern))
126 : {
127 7 : return true;
128 : }
129 : break;
130 : }
131 4 : case SlicePatternItems::ItemType::HAS_REST:
132 4 : {
133 4 : const auto &items_has_rest
134 4 : = static_cast<const SlicePatternItemsHasRest &> (*items);
135 4 : size_t bound_patterns_count
136 4 : = items_has_rest.get_lower_patterns ().size ()
137 4 : + items_has_rest.get_upper_patterns ().size ();
138 4 : auto *capacity_ty = arr.get_capacity ();
139 4 : rust_assert (capacity_ty->get_kind () == TyTy::TypeKind::CONST);
140 4 : auto *capacity_const = capacity_ty->as_const_type ();
141 4 : rust_assert (capacity_const->const_kind ()
142 : == TyTy::BaseConstType::ConstKind::Value);
143 4 : auto &capacity_value
144 : = *static_cast<const TyTy::ConstValueType *> (capacity_const);
145 4 : auto cap_tree = capacity_value.get_value ();
146 4 : rust_assert (!error_operand_p (cap_tree));
147 4 : size_t cap = (size_t) wi::to_wide (cap_tree).to_uhwi ();
148 :
149 4 : if (bound_patterns_count > cap)
150 : return true;
151 8 : for (const auto &pattern : items_has_rest.get_lower_patterns ())
152 4 : if (is_refutable_with_lookup (*pattern))
153 : {
154 7 : return true;
155 : }
156 8 : for (const auto &pattern : items_has_rest.get_upper_patterns ())
157 4 : if (is_refutable_with_lookup (*pattern))
158 : {
159 7 : return true;
160 : }
161 : break;
162 : }
163 : }
164 : return false;
165 : }
166 :
167 : bool
168 23 : TupleStructPattern::is_refutable (const TyTy::BaseType &scrutinee) const
169 : {
170 : // A tuple struct pattern corresponding to an enum with multiple variants is
171 : // always refutable
172 23 : if (scrutinee.get_kind () == TyTy::TypeKind::ADT)
173 : {
174 23 : const auto &adt = static_cast<const TyTy::ADTType &> (scrutinee);
175 31 : if (adt.is_enum () && adt.number_of_variants () > 1)
176 : {
177 : return true;
178 : }
179 : }
180 : // We need to also check the refutability of each item's pattern in the tuple
181 : // struct
182 21 : switch (items->get_item_type ())
183 : {
184 15 : case TupleStructItems::ItemType::NO_REST:
185 17 : for (const auto &pattern :
186 32 : static_cast<TupleStructItemsNoRest &> (*items).get_patterns ())
187 19 : if (is_refutable_with_lookup (*pattern))
188 : {
189 23 : return true;
190 : }
191 : break;
192 6 : case TupleStructItems::ItemType::HAS_REST:
193 6 : auto &items_has_rest = static_cast<TupleStructItemsHasRest &> (*items);
194 10 : for (const auto &pattern : items_has_rest.get_lower_patterns ())
195 6 : if (is_refutable_with_lookup (*pattern))
196 : {
197 23 : return true;
198 : }
199 8 : for (const auto &pattern : items_has_rest.get_upper_patterns ())
200 4 : if (is_refutable_with_lookup (*pattern))
201 : {
202 23 : return true;
203 : }
204 : break;
205 : }
206 : return false;
207 : }
208 :
209 : bool
210 319 : TuplePattern::is_refutable (const TyTy::BaseType &scrutinee) const
211 : {
212 319 : const auto *destructured = scrutinee.destructure ();
213 319 : rust_assert (destructured->get_kind () == TyTy::TypeKind::TUPLE);
214 319 : const auto &tup = static_cast<const TyTy::TupleType &> (*destructured);
215 :
216 319 : switch (items->get_item_type ())
217 : {
218 309 : case TuplePatternItems::ItemType::NO_REST:
219 309 : {
220 309 : auto &no_rest = static_cast<TuplePatternItemsNoRest &> (*items);
221 309 : const auto &patterns = no_rest.get_patterns ();
222 933 : for (size_t i = 0; i < patterns.size (); i++)
223 628 : if (patterns[i]->is_refutable (*tup.get_field (i)))
224 : return true;
225 : break;
226 : }
227 10 : case TuplePatternItems::ItemType::HAS_REST:
228 10 : {
229 10 : auto &has_rest = static_cast<TuplePatternItemsHasRest &> (*items);
230 10 : const auto &lower = has_rest.get_lower_patterns ();
231 10 : const auto &upper = has_rest.get_upper_patterns ();
232 16 : for (size_t i = 0; i < lower.size (); i++)
233 6 : if (lower[i]->is_refutable (*tup.get_field (i)))
234 : return true;
235 10 : size_t base = tup.get_fields ().size () - upper.size ();
236 14 : for (size_t i = 0; i < upper.size (); i++)
237 6 : if (upper[i]->is_refutable (*tup.get_field (base + i)))
238 : return true;
239 : break;
240 : }
241 : }
242 : return false;
243 : }
244 : bool
245 19117 : IdentifierPattern::is_refutable (const TyTy::BaseType &scrutinee) const
246 : {
247 19117 : if (has_subpattern ())
248 : {
249 13 : auto context = Resolver::TypeCheckContext::get ();
250 13 : HirId hir_id = subpattern->get_mappings ().get_hirid ();
251 13 : if (hir_id)
252 : {
253 13 : TyTy::BaseType *ty = nullptr;
254 13 : if (context->lookup_type (hir_id, &ty))
255 : {
256 13 : return subpattern->is_refutable (*ty);
257 : }
258 : else
259 : {
260 0 : rust_internal_error_at (
261 0 : get_locus (),
262 : "failed to lookup hir item during refutability checks");
263 : return true;
264 : }
265 : }
266 : }
267 :
268 : return false;
269 : }
270 :
271 : bool
272 19 : ReferencePattern::is_refutable (const TyTy::BaseType &scrutinee) const
273 : {
274 19 : const auto *inner = scrutinee.destructure ();
275 :
276 19 : rust_assert (inner->get_kind () == TyTy::TypeKind::REF);
277 19 : const auto &ref = static_cast<const TyTy::ReferenceType &> (*inner);
278 19 : return pattern->is_refutable (*ref.get_base ());
279 : }
280 :
281 : bool
282 11 : StructPattern::is_refutable (const TyTy::BaseType &scrutinee) const
283 : {
284 11 : const auto *inner = scrutinee.destructure ();
285 11 : rust_assert (inner->get_kind () == TyTy::TypeKind::ADT);
286 11 : const auto &adt = static_cast<const TyTy::ADTType &> (*inner);
287 11 : rust_assert (!adt.is_enum ());
288 11 : TyTy::VariantDef *variant = adt.get_variants ().at (0);
289 :
290 26 : for (const auto &field : elems.get_struct_pattern_fields ())
291 : {
292 19 : switch (field->get_item_type ())
293 : {
294 0 : case StructPatternField::ItemType::TUPLE_PAT:
295 0 : {
296 0 : const auto &tuple_field
297 0 : = static_cast<const StructPatternFieldTuplePat &> (*field);
298 0 : TyTy::StructFieldType *field_ty
299 0 : = variant->get_field_at_index (tuple_field.get_index ());
300 0 : if (tuple_field.get_tuple_pattern ().is_refutable (
301 0 : *field_ty->get_field_type ()))
302 : return true;
303 : break;
304 : }
305 13 : case StructPatternField::ItemType::IDENT_PAT:
306 13 : {
307 13 : const auto &ident_field
308 13 : = static_cast<const StructPatternFieldIdentPat &> (*field);
309 13 : TyTy::StructFieldType *field_ty = nullptr;
310 13 : bool found = variant->lookup_field (
311 13 : ident_field.get_identifier ().as_string (), &field_ty, nullptr);
312 13 : rust_assert (found);
313 13 : if (ident_field.get_pattern ().is_refutable (
314 13 : *field_ty->get_field_type ()))
315 4 : return true;
316 9 : break;
317 : }
318 : case StructPatternField::ItemType::IDENT:
319 : break;
320 : }
321 : }
322 : return false;
323 : }
324 : } // namespace HIR
325 :
326 : } // namespace Rust
|