Line data Source code
1 : // Copyright (C) 2020-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-casts.h"
20 : #include "rust-tyty-util.h"
21 :
22 : namespace Rust {
23 : namespace Resolver {
24 :
25 5184 : TypeCastRules::TypeCastRules (location_t locus, TyTy::TyWithLocation from,
26 5184 : TyTy::TyWithLocation to)
27 5184 : : locus (locus), from (from), to (to)
28 5184 : {}
29 :
30 : TypeCoercionRules::CoercionResult
31 5184 : TypeCastRules::resolve (location_t locus, TyTy::TyWithLocation from,
32 : TyTy::TyWithLocation to, bool emit_error)
33 : {
34 5184 : TypeCastRules cast_rules (locus, from, to);
35 5184 : return cast_rules.check (emit_error);
36 : }
37 :
38 : TypeCoercionRules::CoercionResult
39 5184 : TypeCastRules::check (bool emit_error)
40 : {
41 : // try the simple cast rules
42 5184 : auto simple_cast = cast_rules ();
43 5184 : if (!simple_cast.is_error ())
44 3350 : return simple_cast;
45 :
46 : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L565-L582
47 1834 : auto possible_coercion
48 : = TypeCoercionRules::TryCoerce (from.get_ty (), to.get_ty (), locus,
49 : true /*allow-autoderef*/,
50 1834 : true /*is_cast_site*/);
51 1834 : if (!possible_coercion.is_error ())
52 : {
53 : // given the attempt was ok we need to ensure we perform it so that any
54 : // inference variables are unified correctly
55 1811 : return TypeCoercionRules::Coerce (from.get_ty (), to.get_ty (), locus,
56 : true /*allow-autoderef*/,
57 1811 : true /*is_cast_site*/);
58 : }
59 :
60 23 : if (emit_error)
61 16 : TypeCastRules::emit_cast_error (locus, from, to);
62 :
63 23 : return TypeCoercionRules::CoercionResult::get_error ();
64 1834 : }
65 :
66 : TypeCoercionRules::CoercionResult
67 5184 : TypeCastRules::cast_rules ()
68 : {
69 : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L596
70 : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L654
71 :
72 5184 : TyTy::BaseType *from_type = from.get_ty ()->destructure ();
73 :
74 5184 : rust_debug ("cast_rules from={%s} to={%s}", from_type->debug_str ().c_str (),
75 : to.get_ty ()->debug_str ().c_str ());
76 5184 : switch (from_type->get_kind ())
77 : {
78 72 : case TyTy::TypeKind::INFER:
79 72 : {
80 72 : TyTy::InferType *from_infer
81 : = static_cast<TyTy::InferType *> (from_type);
82 72 : switch (from_infer->get_infer_kind ())
83 : {
84 0 : case TyTy::InferType::InferTypeKind::GENERAL:
85 0 : return TypeCoercionRules::CoercionResult{{},
86 0 : to.get_ty ()->clone ()};
87 :
88 72 : case TyTy::InferType::InferTypeKind::INTEGRAL:
89 72 : switch (to.get_ty ()->get_kind ())
90 : {
91 2 : case TyTy::TypeKind::CHAR:
92 2 : {
93 : // only u8 and char
94 2 : bool was_uint
95 2 : = from.get_ty ()->get_kind () == TyTy::TypeKind::UINT;
96 2 : bool was_u8
97 : = was_uint
98 2 : && (static_cast<TyTy::UintType *> (from.get_ty ())
99 0 : ->get_uint_kind ()
100 8 : == TyTy::UintType::UintKind::U8);
101 0 : if (was_u8)
102 0 : return TypeCoercionRules::CoercionResult{
103 0 : {}, to.get_ty ()->clone ()};
104 : }
105 : break;
106 :
107 53 : case TyTy::TypeKind::USIZE:
108 53 : case TyTy::TypeKind::ISIZE:
109 53 : case TyTy::TypeKind::UINT:
110 53 : case TyTy::TypeKind::INT:
111 53 : case TyTy::TypeKind::POINTER:
112 53 : return TypeCoercionRules::CoercionResult{
113 53 : {}, to.get_ty ()->clone ()};
114 :
115 16 : case TyTy::TypeKind::INFER:
116 16 : {
117 16 : TyTy::InferType *to_infer
118 16 : = static_cast<TyTy::InferType *> (to.get_ty ());
119 :
120 16 : switch (to_infer->get_infer_kind ())
121 : {
122 16 : case TyTy::InferType::InferTypeKind::GENERAL:
123 16 : case TyTy::InferType::InferTypeKind::INTEGRAL:
124 16 : return TypeCoercionRules::CoercionResult{
125 16 : {}, to.get_ty ()->clone ()};
126 :
127 0 : default:
128 0 : return TypeCoercionRules::CoercionResult::get_error ();
129 : }
130 : }
131 1 : break;
132 :
133 1 : default:
134 1 : return TypeCoercionRules::CoercionResult::get_error ();
135 : }
136 : break;
137 :
138 0 : case TyTy::InferType::InferTypeKind::FLOAT:
139 0 : switch (to.get_ty ()->get_kind ())
140 : {
141 0 : case TyTy::TypeKind::USIZE:
142 0 : case TyTy::TypeKind::ISIZE:
143 0 : case TyTy::TypeKind::UINT:
144 0 : case TyTy::TypeKind::INT:
145 0 : return TypeCoercionRules::CoercionResult{
146 0 : {}, to.get_ty ()->clone ()};
147 :
148 0 : case TyTy::TypeKind::INFER:
149 0 : {
150 0 : TyTy::InferType *to_infer
151 0 : = static_cast<TyTy::InferType *> (to.get_ty ());
152 :
153 0 : switch (to_infer->get_infer_kind ())
154 : {
155 0 : case TyTy::InferType::InferTypeKind::GENERAL:
156 0 : case TyTy::InferType::InferTypeKind::FLOAT:
157 0 : return TypeCoercionRules::CoercionResult{
158 0 : {}, to.get_ty ()->clone ()};
159 :
160 0 : default:
161 0 : return TypeCoercionRules::CoercionResult::get_error ();
162 : }
163 : }
164 0 : break;
165 :
166 0 : default:
167 0 : return TypeCoercionRules::CoercionResult::get_error ();
168 : }
169 : break;
170 : }
171 : }
172 : break;
173 :
174 52 : case TyTy::TypeKind::BOOL:
175 52 : switch (to.get_ty ()->get_kind ())
176 : {
177 49 : case TyTy::TypeKind::INFER:
178 49 : case TyTy::TypeKind::USIZE:
179 49 : case TyTy::TypeKind::ISIZE:
180 49 : case TyTy::TypeKind::UINT:
181 49 : case TyTy::TypeKind::INT:
182 49 : return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
183 :
184 3 : default:
185 3 : return TypeCoercionRules::CoercionResult::get_error ();
186 : }
187 1256 : break;
188 :
189 1256 : case TyTy::TypeKind::CHAR:
190 1256 : case TyTy::TypeKind::USIZE:
191 1256 : case TyTy::TypeKind::ISIZE:
192 1256 : case TyTy::TypeKind::UINT:
193 1256 : case TyTy::TypeKind::INT:
194 1256 : switch (to.get_ty ()->get_kind ())
195 : {
196 20 : case TyTy::TypeKind::CHAR:
197 20 : {
198 : // only u8 and char
199 20 : bool was_uint = from.get_ty ()->get_kind () == TyTy::TypeKind::UINT;
200 20 : bool was_u8 = was_uint
201 20 : && (static_cast<TyTy::UintType *> (from.get_ty ())
202 18 : ->get_uint_kind ()
203 24 : == TyTy::UintType::UintKind::U8);
204 16 : if (was_u8)
205 16 : return TypeCoercionRules::CoercionResult{{},
206 16 : to.get_ty ()->clone ()};
207 : }
208 : break;
209 :
210 10 : case TyTy::TypeKind::FLOAT:
211 10 : {
212 : // can only do this for number types not char
213 10 : bool from_char
214 10 : = from.get_ty ()->get_kind () == TyTy::TypeKind::CHAR;
215 10 : if (!from_char)
216 9 : return TypeCoercionRules::CoercionResult{{},
217 9 : to.get_ty ()->clone ()};
218 : }
219 : break;
220 :
221 6 : case TyTy::TypeKind::POINTER:
222 6 : {
223 : // char can't be casted as a ptr
224 6 : bool from_char
225 6 : = from.get_ty ()->get_kind () == TyTy::TypeKind::CHAR;
226 6 : if (!from_char)
227 6 : return TypeCoercionRules::CoercionResult{{},
228 6 : to.get_ty ()->clone ()};
229 : }
230 : break;
231 :
232 1217 : case TyTy::TypeKind::INFER:
233 1217 : case TyTy::TypeKind::USIZE:
234 1217 : case TyTy::TypeKind::ISIZE:
235 1217 : case TyTy::TypeKind::UINT:
236 1217 : case TyTy::TypeKind::INT:
237 1217 : return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
238 :
239 3 : default:
240 3 : return TypeCoercionRules::CoercionResult::get_error ();
241 : }
242 : break;
243 :
244 6 : case TyTy::TypeKind::FLOAT:
245 6 : switch (to.get_ty ()->get_kind ())
246 : {
247 6 : case TyTy::TypeKind::USIZE:
248 6 : case TyTy::TypeKind::ISIZE:
249 6 : case TyTy::TypeKind::UINT:
250 6 : case TyTy::TypeKind::INT:
251 6 : return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
252 :
253 0 : case TyTy::TypeKind::FLOAT:
254 0 : return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
255 :
256 0 : case TyTy::TypeKind::INFER:
257 0 : {
258 0 : TyTy::InferType *to_infer
259 0 : = static_cast<TyTy::InferType *> (to.get_ty ());
260 :
261 0 : switch (to_infer->get_infer_kind ())
262 : {
263 0 : case TyTy::InferType::InferTypeKind::GENERAL:
264 0 : case TyTy::InferType::InferTypeKind::FLOAT:
265 0 : return TypeCoercionRules::CoercionResult{
266 0 : {}, to.get_ty ()->clone ()};
267 :
268 0 : default:
269 0 : return TypeCoercionRules::CoercionResult::get_error ();
270 : }
271 : }
272 0 : break;
273 :
274 0 : default:
275 0 : return TypeCoercionRules::CoercionResult::get_error ();
276 : }
277 3791 : break;
278 :
279 3791 : case TyTy::TypeKind::REF:
280 3791 : case TyTy::TypeKind::FNPTR:
281 3791 : case TyTy::TypeKind::POINTER:
282 3791 : switch (to.get_ty ()->get_kind ())
283 : {
284 19 : case TyTy::TypeKind::USIZE:
285 19 : case TyTy::TypeKind::ISIZE:
286 19 : case TyTy::TypeKind::UINT:
287 19 : case TyTy::TypeKind::INT:
288 19 : {
289 : // refs should not cast to numeric type
290 19 : auto kind = from.get_ty ()->get_kind ();
291 19 : bool from_ptr = kind == TyTy::TypeKind::POINTER
292 19 : || kind == TyTy::TypeKind::FNPTR;
293 19 : if (from_ptr)
294 : {
295 18 : return TypeCoercionRules::CoercionResult{
296 18 : {}, to.get_ty ()->clone ()};
297 : }
298 : }
299 : break;
300 :
301 3771 : case TyTy::TypeKind::REF:
302 3771 : case TyTy::TypeKind::POINTER:
303 3771 : return check_ptr_ptr_cast ();
304 :
305 1 : default:
306 1 : return TypeCoercionRules::CoercionResult::get_error ();
307 : }
308 : break;
309 :
310 7 : default:
311 7 : return TypeCoercionRules::CoercionResult::get_error ();
312 : }
313 :
314 8 : return TypeCoercionRules::CoercionResult::get_error ();
315 : }
316 :
317 : TypeCoercionRules::CoercionResult
318 3771 : TypeCastRules::check_ptr_ptr_cast ()
319 : {
320 3771 : rust_debug ("check_ptr_ptr_cast from={%s} to={%s}",
321 : from.get_ty ()->debug_str ().c_str (),
322 : to.get_ty ()->debug_str ().c_str ());
323 :
324 3771 : bool from_is_ref = from.get_ty ()->get_kind () == TyTy::TypeKind::REF;
325 3771 : bool to_is_ref = to.get_ty ()->get_kind () == TyTy::TypeKind::REF;
326 3771 : bool from_is_ptr = from.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
327 3771 : bool to_is_ptr = to.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
328 :
329 3771 : if (from_is_ptr && to_is_ptr)
330 : {
331 : // mutability is ignored here as all pointer usage requires unsafe
332 1960 : return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
333 : }
334 1811 : else if (from_is_ref && to_is_ref)
335 : {
336 14 : const auto &from_ref = *from.get_ty ()->as<TyTy::ReferenceType> ();
337 14 : const auto &to_ref = *to.get_ty ()->as<TyTy::ReferenceType> ();
338 :
339 14 : if (from_ref.is_dyn_object () != to_ref.is_dyn_object ())
340 : {
341 : // this needs to be handled by coercion logic
342 14 : return TypeCoercionRules::CoercionResult::get_error ();
343 : }
344 :
345 : // are the underlying types safely simple castable?
346 7 : const auto to_underly = to_ref.get_base ();
347 7 : const auto from_underly = from_ref.get_base ();
348 7 : auto res = resolve (locus, TyTy::TyWithLocation (from_underly),
349 14 : TyTy::TyWithLocation (to_underly), false);
350 7 : if (res.is_error ())
351 : {
352 : // this needs to be handled by coercion logic
353 7 : return TypeCoercionRules::CoercionResult::get_error ();
354 : }
355 :
356 : // mutability must be coerceable
357 0 : TyTy::ReferenceType &f
358 0 : = static_cast<TyTy::ReferenceType &> (*from.get_ty ());
359 0 : TyTy::ReferenceType &t
360 0 : = static_cast<TyTy::ReferenceType &> (*to.get_ty ());
361 :
362 0 : if (TypeCoercionRules::coerceable_mutability (f.mutability (),
363 : t.mutability ()))
364 : {
365 0 : return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
366 : }
367 7 : }
368 :
369 1797 : return TypeCoercionRules::CoercionResult::get_error ();
370 : }
371 :
372 : void
373 16 : TypeCastRules::emit_cast_error (location_t locus, TyTy::TyWithLocation from,
374 : TyTy::TyWithLocation to)
375 : {
376 16 : rich_location r (line_table, locus);
377 16 : r.add_range (from.get_locus ());
378 16 : r.add_range (to.get_locus ());
379 16 : ErrorCode error_code;
380 16 : std::string error_msg;
381 16 : switch (to.get_ty ()->get_kind ())
382 : {
383 4 : case TyTy::TypeKind::BOOL:
384 4 : error_msg = "cannot cast %qs as %qs";
385 4 : error_code = ErrorCode::E0054;
386 4 : break;
387 7 : case TyTy::TypeKind::CHAR:
388 7 : error_msg
389 7 : += "cannot cast %qs as %qs, only %<u8%> can be cast as %<char%>";
390 7 : error_code = ErrorCode::E0604;
391 7 : break;
392 1 : case TyTy::TypeKind::SLICE:
393 1 : error_msg = "cast to unsized type: %qs as %qs";
394 1 : error_code = ErrorCode::E0620;
395 1 : break;
396 :
397 4 : default:
398 4 : error_msg = "casting %qs as %qs is invalid";
399 4 : error_code = ErrorCode::E0606;
400 4 : break;
401 : }
402 16 : rust_error_at (r, error_code, error_msg.c_str (),
403 32 : from.get_ty ()->get_name ().c_str (),
404 32 : to.get_ty ()->get_name ().c_str ());
405 16 : }
406 :
407 : } // namespace Resolver
408 : } // namespace Rust
|