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