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