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