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