Branch data Line data Source code
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-coercion.h"
20 : : #include "rust-type-util.h"
21 : :
22 : : namespace Rust {
23 : : namespace Resolver {
24 : :
25 : : TypeCoercionRules::CoercionResult
26 : 39229 : TypeCoercionRules::Coerce (TyTy::BaseType *receiver, TyTy::BaseType *expected,
27 : : location_t locus, bool allow_autoderef,
28 : : bool is_cast_site)
29 : : {
30 : 39229 : TypeCoercionRules resolver (expected, locus, true, allow_autoderef, false,
31 : 39229 : is_cast_site);
32 : 39229 : bool ok = resolver.do_coercion (receiver);
33 : 78403 : return ok ? resolver.try_result : CoercionResult::get_error ();
34 : 39229 : }
35 : :
36 : : TypeCoercionRules::CoercionResult
37 : 34618 : TypeCoercionRules::TryCoerce (TyTy::BaseType *receiver,
38 : : TyTy::BaseType *expected, location_t locus,
39 : : bool allow_autoderef, bool is_cast_site)
40 : : {
41 : 34618 : TypeCoercionRules resolver (expected, locus, false, allow_autoderef, true,
42 : 34618 : is_cast_site);
43 : 34618 : bool ok = resolver.do_coercion (receiver);
44 : 43926 : return ok ? resolver.try_result : CoercionResult::get_error ();
45 : 34618 : }
46 : :
47 : 73847 : TypeCoercionRules::TypeCoercionRules (TyTy::BaseType *expected,
48 : : location_t locus, bool emit_errors,
49 : : bool allow_autoderef, bool try_flag,
50 : 73847 : bool is_cast_site)
51 : 221541 : : AutoderefCycle (!allow_autoderef), mappings (Analysis::Mappings::get ()),
52 : 73847 : context (TypeCheckContext::get ()), expected (expected), locus (locus),
53 : 73847 : try_result (CoercionResult::get_error ()), emit_errors (emit_errors),
54 : 73847 : try_flag (try_flag), is_cast_site (is_cast_site)
55 : 73847 : {}
56 : :
57 : : bool
58 : 73847 : TypeCoercionRules::do_coercion (TyTy::BaseType *receiver)
59 : : {
60 : : // FIXME this is not finished and might be super simplified
61 : : // see:
62 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs
63 : :
64 : : // handle never
65 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L155
66 : 73847 : if (receiver->get_kind () == TyTy::TypeKind::NEVER)
67 : : {
68 : : // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
69 : : // type variable, we want `?T` to fallback to `!` if not
70 : : // otherwise constrained. An example where this arises:
71 : : //
72 : : // let _: Option<?T> = Some({ return; });
73 : : //
74 : : // here, we would coerce from `!` to `?T`.
75 : 401 : if (expected->has_substitutions_defined () && !expected->is_concrete ())
76 : : {
77 : 1 : location_t locus = mappings.lookup_location (receiver->get_ref ());
78 : 1 : TyTy::TyVar implicit_var
79 : 1 : = TyTy::TyVar::get_implicit_infer_var (locus);
80 : 1 : try_result = CoercionResult{{}, implicit_var.get_tyty ()};
81 : 1 : return true;
82 : : }
83 : : else
84 : : {
85 : 400 : bool expected_is_infer_var
86 : 400 : = expected->get_kind () == TyTy::TypeKind::INFER;
87 : 400 : bool expected_is_general_infer_var
88 : : = expected_is_infer_var
89 : 400 : && (static_cast<TyTy::InferType *> (expected)->get_infer_kind ()
90 : 400 : == TyTy::InferType::InferTypeKind::GENERAL);
91 : :
92 : : // FIXME this 'expected_is_general_infer_var' case needs to eventually
93 : : // should go away see: compile/never_type_err1.rs
94 : : //
95 : : // I think we need inference obligations to say that yes we have a
96 : : // general inference variable but we add the oligation to the expected
97 : : // type that it could default to '!'
98 : 400 : if (expected_is_general_infer_var)
99 : 1 : try_result = CoercionResult{{}, receiver};
100 : : else
101 : 399 : try_result = CoercionResult{{}, expected->clone ()};
102 : :
103 : 400 : return true;
104 : : }
105 : : }
106 : :
107 : : // unsize
108 : 73446 : bool unsafe_error = false;
109 : 73446 : CoercionResult unsize_coercion
110 : 73446 : = coerce_unsized (receiver, expected, unsafe_error);
111 : 73446 : bool valid_unsize_coercion = !unsize_coercion.is_error ();
112 : 73446 : if (valid_unsize_coercion)
113 : : {
114 : 133 : try_result = unsize_coercion;
115 : 133 : return true;
116 : : }
117 : 73313 : else if (unsafe_error)
118 : : {
119 : : // location_t lhs = mappings.lookup_location (receiver->get_ref ());
120 : : // location_t rhs = mappings.lookup_location (expected->get_ref ());
121 : : // object_unsafe_error (locus, lhs, rhs);
122 : : return false;
123 : : }
124 : :
125 : : // pointers
126 : 73140 : switch (expected->get_kind ())
127 : : {
128 : 7243 : case TyTy::TypeKind::POINTER:
129 : 7243 : {
130 : 7243 : TyTy::PointerType *ptr = static_cast<TyTy::PointerType *> (expected);
131 : 7243 : try_result = coerce_unsafe_ptr (receiver, ptr, ptr->mutability ());
132 : 7243 : return !try_result.is_error ();
133 : : }
134 : :
135 : 22831 : case TyTy::TypeKind::REF:
136 : 22831 : {
137 : 22831 : TyTy::ReferenceType *ptr
138 : : = static_cast<TyTy::ReferenceType *> (expected);
139 : 22831 : try_result
140 : 22831 : = coerce_borrowed_pointer (receiver, ptr, ptr->mutability ());
141 : 22831 : return !try_result.is_error ();
142 : : }
143 : 43066 : break;
144 : :
145 : 43066 : default:
146 : 43066 : break;
147 : : }
148 : :
149 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L210
150 : 43066 : switch (receiver->get_kind ())
151 : : {
152 : 43066 : default:
153 : 43066 : {
154 : 43066 : rust_debug (
155 : : "do_coercion default unify and infer expected: %s receiver %s",
156 : : receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
157 : 43066 : TyTy::BaseType *result
158 : 43066 : = unify_site_and (receiver->get_ref (),
159 : 43066 : TyTy::TyWithLocation (expected),
160 : 43066 : TyTy::TyWithLocation (receiver),
161 : : locus /*unify_locus*/, false /*emit_errors*/,
162 : 43066 : !try_flag /*commit_if_ok*/, try_flag /*infer*/,
163 : 43066 : try_flag /*cleanup on error*/);
164 : 43066 : if (result->get_kind () != TyTy::TypeKind::ERROR)
165 : : {
166 : 33076 : try_result = CoercionResult{{}, result};
167 : 33076 : return true;
168 : : }
169 : : }
170 : 9990 : break;
171 : : }
172 : :
173 : 9990 : return !try_result.is_error ();
174 : 73446 : }
175 : :
176 : : TypeCoercionRules::CoercionResult
177 : 7243 : TypeCoercionRules::coerce_unsafe_ptr (TyTy::BaseType *receiver,
178 : : TyTy::PointerType *expected,
179 : : Mutability to_mutbl)
180 : : {
181 : 7243 : rust_debug ("coerce_unsafe_ptr(receiver={%s}, expected={%s})",
182 : : receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
183 : :
184 : 7243 : Mutability from_mutbl = Mutability::Imm;
185 : 7243 : TyTy::BaseType *element = nullptr;
186 : 7243 : switch (receiver->get_kind ())
187 : : {
188 : 3818 : case TyTy::TypeKind::REF:
189 : 3818 : {
190 : 3818 : TyTy::ReferenceType *ref
191 : : = static_cast<TyTy::ReferenceType *> (receiver);
192 : 3818 : from_mutbl = ref->mutability ();
193 : 3818 : element = ref->get_base ();
194 : : }
195 : 3818 : break;
196 : :
197 : 3285 : case TyTy::TypeKind::POINTER:
198 : 3285 : {
199 : 3285 : TyTy::PointerType *ref = static_cast<TyTy::PointerType *> (receiver);
200 : 3285 : from_mutbl = ref->mutability ();
201 : 3285 : element = ref->get_base ();
202 : : }
203 : 3285 : break;
204 : :
205 : 140 : default:
206 : 140 : {
207 : 140 : if (types_compatable (TyTy::TyWithLocation (receiver),
208 : 140 : TyTy::TyWithLocation (expected), UNKNOWN_LOCATION,
209 : : false))
210 : 0 : return CoercionResult{{}, expected->clone ()};
211 : :
212 : 140 : return CoercionResult::get_error ();
213 : : }
214 : : }
215 : :
216 : 7103 : bool receiver_is_non_ptr = receiver->get_kind () != TyTy::TypeKind::POINTER;
217 : 7103 : if (autoderef_flag && receiver_is_non_ptr)
218 : : {
219 : : // it is unsafe to autoderef to raw pointers
220 : 14 : return CoercionResult::get_error ();
221 : : }
222 : :
223 : 7089 : if (!coerceable_mutability (from_mutbl, to_mutbl))
224 : : {
225 : 0 : location_t lhs = mappings.lookup_location (receiver->get_ref ());
226 : 0 : location_t rhs = mappings.lookup_location (expected->get_ref ());
227 : 0 : mismatched_mutability_error (locus, lhs, rhs);
228 : 0 : return TypeCoercionRules::CoercionResult::get_error ();
229 : : }
230 : :
231 : 7089 : TyTy::PointerType *coerced_mutability
232 : : = new TyTy::PointerType (receiver->get_ref (),
233 : 7089 : TyTy::TyVar (element->get_ref ()), to_mutbl);
234 : :
235 : 7089 : rust_debug ("coerce_unsafe_ptr unify-site");
236 : :
237 : : // this is a really annoying case rust allows casts of any ptr to another ptr
238 : : // types
239 : : //
240 : : // *? vs *i32 - simple coercion valid
241 : : // *? vs *T - simple coercion valid
242 : : // *i32 vs *i32 - simple coercion valid
243 : : // *i32 vs *u8 - simple coercion not valid but allowed in cast site
244 : : // *T vs *u8 - not valid but is allowed in cast site
245 : :
246 : 7089 : TyTy::BaseType *result
247 : 7089 : = unify_site_and (receiver->get_ref (), TyTy::TyWithLocation (expected),
248 : 7089 : TyTy::TyWithLocation (coerced_mutability),
249 : : locus /*unify_locus*/, !try_flag /*emit_errors*/,
250 : 7089 : !try_flag /*commit_if_ok*/,
251 : 2088 : try_flag && !is_cast_site /*infer*/,
252 : 7089 : try_flag /*cleanup on error*/);
253 : 7089 : bool unsafe_ptr_coerceion_ok = result->get_kind () != TyTy::TypeKind::ERROR;
254 : 7089 : if (unsafe_ptr_coerceion_ok)
255 : 7026 : return CoercionResult{{}, result};
256 : :
257 : 63 : return TypeCoercionRules::CoercionResult::get_error ();
258 : : }
259 : :
260 : : /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
261 : : /// To match `A` with `B`, autoderef will be performed,
262 : : /// calling `deref`/`deref_mut` where necessary.
263 : : TypeCoercionRules::CoercionResult
264 : 22831 : TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver,
265 : : TyTy::ReferenceType *expected,
266 : : Mutability to_mutbl)
267 : : {
268 : 22831 : rust_debug ("coerce_borrowed_pointer(a={%s}, b={%s})",
269 : : receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
270 : :
271 : 22831 : Mutability from_mutbl = Mutability::Imm;
272 : 22831 : switch (receiver->get_kind ())
273 : : {
274 : 10604 : case TyTy::TypeKind::REF:
275 : 10604 : {
276 : 10604 : TyTy::ReferenceType *from
277 : : = static_cast<TyTy::ReferenceType *> (receiver);
278 : 10604 : from_mutbl = from->mutability ();
279 : : }
280 : 10604 : break;
281 : :
282 : 12227 : default:
283 : 12227 : {
284 : 12227 : rust_debug ("coerce_borrowed_pointer -- unify");
285 : 12227 : TyTy::BaseType *result
286 : 12227 : = unify_site_and (receiver->get_ref (),
287 : 12227 : TyTy::TyWithLocation (receiver),
288 : 12227 : TyTy::TyWithLocation (expected), locus,
289 : 12227 : false /*emit_errors*/, !try_flag /*commit_if_ok*/,
290 : : try_flag /* infer */,
291 : 12227 : try_flag /*cleanup_on_failure*/);
292 : 12227 : bool default_coerceion_ok
293 : 12227 : = result->get_kind () != TyTy::TypeKind::ERROR;
294 : 12227 : if (default_coerceion_ok)
295 : 21 : return CoercionResult{{}, result};
296 : :
297 : 12206 : return TypeCoercionRules::CoercionResult::get_error ();
298 : : }
299 : : }
300 : :
301 : 10604 : if (!coerceable_mutability (from_mutbl, to_mutbl))
302 : : {
303 : 0 : location_t lhs = mappings.lookup_location (receiver->get_ref ());
304 : 0 : location_t rhs = mappings.lookup_location (expected->get_ref ());
305 : 0 : mismatched_mutability_error (locus, lhs, rhs);
306 : 0 : return TypeCoercionRules::CoercionResult::get_error ();
307 : : }
308 : :
309 : 10604 : rust_debug ("coerce_borrowed_pointer -- autoderef cycle");
310 : 10604 : AutoderefCycle::cycle (receiver);
311 : 18429 : rust_debug ("coerce_borrowed_pointer -- result: [%s] with adjustments: [%zu]",
312 : : try_result.is_error () ? "failed" : "matched",
313 : : try_result.adjustments.size ());
314 : :
315 : 10604 : return try_result;
316 : : }
317 : :
318 : : // &[T; n] or &mut [T; n] -> &[T]
319 : : // or &mut [T; n] -> &mut [T]
320 : : // or &Concrete -> &Trait, etc.
321 : : TypeCoercionRules::CoercionResult
322 : 73446 : TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
323 : : TyTy::BaseType *target, bool &unsafe_error)
324 : : {
325 : 73446 : rust_debug ("coerce_unsized(source={%s}, target={%s})",
326 : : source->debug_str ().c_str (), target->debug_str ().c_str ());
327 : :
328 : 73446 : bool source_is_ref = source->get_kind () == TyTy::TypeKind::REF;
329 : 73446 : bool target_is_ref = target->get_kind () == TyTy::TypeKind::REF;
330 : 73446 : bool target_is_ptr = target->get_kind () == TyTy::TypeKind::POINTER;
331 : :
332 : 73446 : bool needs_reborrow = false;
333 : 73446 : TyTy::BaseType *ty_a = source;
334 : 73446 : TyTy::BaseType *ty_b = target;
335 : 73446 : Mutability expected_mutability = Mutability::Imm;
336 : 73446 : if (source_is_ref && target_is_ref)
337 : : {
338 : 10910 : TyTy::ReferenceType *source_ref
339 : : = static_cast<TyTy::ReferenceType *> (source);
340 : 10910 : TyTy::ReferenceType *target_ref
341 : : = static_cast<TyTy::ReferenceType *> (target);
342 : :
343 : 10910 : Mutability from_mutbl = source_ref->mutability ();
344 : 10910 : Mutability to_mutbl = target_ref->mutability ();
345 : 10910 : if (!coerceable_mutability (from_mutbl, to_mutbl))
346 : : {
347 : 172 : unsafe_error = true;
348 : 172 : location_t lhs = mappings.lookup_location (source->get_ref ());
349 : 172 : location_t rhs = mappings.lookup_location (target->get_ref ());
350 : 172 : mismatched_mutability_error (locus, lhs, rhs);
351 : 172 : return TypeCoercionRules::CoercionResult::get_error ();
352 : : }
353 : :
354 : 10738 : ty_a = source_ref->get_base ();
355 : 10738 : ty_b = target_ref->get_base ();
356 : 10738 : needs_reborrow = true;
357 : 10738 : expected_mutability = to_mutbl;
358 : :
359 : 10738 : adjustments.push_back (
360 : 10738 : Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
361 : : }
362 : 62536 : else if (source_is_ref && target_is_ptr)
363 : : {
364 : 3818 : TyTy::ReferenceType *source_ref
365 : : = static_cast<TyTy::ReferenceType *> (source);
366 : 3818 : TyTy::PointerType *target_ref = static_cast<TyTy::PointerType *> (target);
367 : :
368 : 3818 : Mutability from_mutbl = source_ref->mutability ();
369 : 3818 : Mutability to_mutbl = target_ref->mutability ();
370 : 3818 : if (!coerceable_mutability (from_mutbl, to_mutbl))
371 : : {
372 : 0 : unsafe_error = true;
373 : 0 : location_t lhs = mappings.lookup_location (source->get_ref ());
374 : 0 : location_t rhs = mappings.lookup_location (target->get_ref ());
375 : 0 : mismatched_mutability_error (locus, lhs, rhs);
376 : 0 : return TypeCoercionRules::CoercionResult::get_error ();
377 : : }
378 : :
379 : 3818 : ty_a = source_ref->get_base ();
380 : 3818 : ty_b = target_ref->get_base ();
381 : 3818 : needs_reborrow = true;
382 : 3818 : expected_mutability = to_mutbl;
383 : :
384 : 3818 : adjustments.push_back (
385 : 3818 : Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
386 : : }
387 : :
388 : : // FIXME
389 : : // there is a bunch of code to ensure something is coerce able to a dyn trait
390 : : // we need to support but we need to support a few more lang items for that
391 : : // see:
392 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L582
393 : :
394 : 73274 : const auto a = ty_a;
395 : 73274 : const auto b = ty_b;
396 : :
397 : 73274 : bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC;
398 : 73274 : bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC;
399 : :
400 : 73274 : if (expect_dyn && need_unsize)
401 : : {
402 : 134 : bool bounds_compatible = b->bounds_compatible (*a, locus, false);
403 : 134 : if (!bounds_compatible)
404 : : {
405 : 1 : unsafe_error = true;
406 : 1 : return TypeCoercionRules::CoercionResult::get_error ();
407 : : }
408 : :
409 : : // return the unsize coercion
410 : 133 : TyTy::BaseType *result = b->clone ();
411 : : // result->set_ref (a->get_ref ());
412 : :
413 : : // append a dyn coercion adjustment
414 : 133 : adjustments.push_back (Adjustment (Adjustment::UNSIZE, a, result));
415 : :
416 : : // reborrow if needed
417 : 133 : if (needs_reborrow)
418 : : {
419 : 133 : TyTy::ReferenceType *reborrow
420 : : = new TyTy::ReferenceType (source->get_ref (),
421 : 133 : TyTy::TyVar (result->get_ref ()),
422 : 266 : expected_mutability);
423 : :
424 : 0 : Adjustment::AdjustmentType borrow_type
425 : 133 : = expected_mutability == Mutability::Imm ? Adjustment::IMM_REF
426 : : : Adjustment::MUT_REF;
427 : 133 : adjustments.push_back (Adjustment (borrow_type, result, reborrow));
428 : 133 : result = reborrow;
429 : : }
430 : :
431 : 133 : return CoercionResult{adjustments, result};
432 : : }
433 : :
434 : 73140 : adjustments.clear ();
435 : 73140 : return TypeCoercionRules::CoercionResult::get_error ();
436 : : }
437 : :
438 : : bool
439 : 16667 : TypeCoercionRules::select (TyTy::BaseType &autoderefed)
440 : : {
441 : 16667 : rust_debug ("TypeCoercionRules::select autoderefed={%s} can_eq expected={%s}",
442 : : autoderefed.debug_str ().c_str (),
443 : : expected->debug_str ().c_str ());
444 : :
445 : 16667 : TyTy::BaseType *result
446 : 16667 : = unify_site_and (autoderefed.get_ref (), TyTy::TyWithLocation (expected),
447 : 16667 : TyTy::TyWithLocation (&autoderefed),
448 : : UNDEF_LOCATION /* locus */, false /*emit_errors*/,
449 : : false /*commit_if_ok*/, true /*infer*/, true /*cleanup*/);
450 : 16667 : bool ok = result->get_kind () != TyTy::TypeKind::ERROR;
451 : 16667 : if (!ok)
452 : : return false;
453 : :
454 : 7825 : try_result = CoercionResult{adjustments, result};
455 : 7825 : return true;
456 : : }
457 : :
458 : : /// Coercing a mutable reference to an immutable works, while
459 : : /// coercing `&T` to `&mut T` should be forbidden.
460 : : bool
461 : 32421 : TypeCoercionRules::coerceable_mutability (Mutability from_mutbl,
462 : : Mutability to_mutbl)
463 : : {
464 : 32421 : return to_mutbl == Mutability::Imm || (from_mutbl == to_mutbl);
465 : : }
466 : :
467 : : void
468 : 172 : TypeCoercionRules::mismatched_mutability_error (location_t expr_locus,
469 : : location_t lhs, location_t rhs)
470 : : {
471 : 172 : if (!emit_errors)
472 : 171 : return;
473 : :
474 : 1 : rich_location r (line_table, expr_locus);
475 : 1 : r.add_range (lhs);
476 : 1 : r.add_range (rhs);
477 : 1 : rust_error_at (r, "mismatched mutability");
478 : 1 : }
479 : :
480 : : void
481 : 0 : TypeCoercionRules::object_unsafe_error (location_t expr_locus, location_t lhs,
482 : : location_t rhs)
483 : : {
484 : 0 : if (!emit_errors)
485 : 0 : return;
486 : :
487 : 0 : rich_location r (line_table, expr_locus);
488 : 0 : r.add_range (lhs);
489 : 0 : r.add_range (rhs);
490 : 0 : rust_error_at (r, "unsafe unsize coercion");
491 : 0 : }
492 : :
493 : : } // namespace Resolver
494 : : } // namespace Rust
|