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 : 39971 : TypeCoercionRules::Coerce (TyTy::BaseType *receiver, TyTy::BaseType *expected,
27 : : location_t locus, bool allow_autoderef,
28 : : bool is_cast_site)
29 : : {
30 : 39971 : TypeCoercionRules resolver (expected, locus, true, allow_autoderef, false,
31 : 39971 : is_cast_site);
32 : 39971 : bool ok = resolver.do_coercion (receiver);
33 : 79885 : return ok ? resolver.try_result : CoercionResult::get_error ();
34 : 39971 : }
35 : :
36 : : TypeCoercionRules::CoercionResult
37 : 34884 : TypeCoercionRules::TryCoerce (TyTy::BaseType *receiver,
38 : : TyTy::BaseType *expected, location_t locus,
39 : : bool allow_autoderef, bool is_cast_site)
40 : : {
41 : 34884 : TypeCoercionRules resolver (expected, locus, false, allow_autoderef, true,
42 : 34884 : is_cast_site);
43 : 34884 : bool ok = resolver.do_coercion (receiver);
44 : 44297 : return ok ? resolver.try_result : CoercionResult::get_error ();
45 : 34884 : }
46 : :
47 : 74855 : TypeCoercionRules::TypeCoercionRules (TyTy::BaseType *expected,
48 : : location_t locus, bool emit_errors,
49 : : bool allow_autoderef, bool try_flag,
50 : 74855 : bool is_cast_site)
51 : 224565 : : AutoderefCycle (!allow_autoderef), mappings (Analysis::Mappings::get ()),
52 : 74855 : context (TypeCheckContext::get ()), expected (expected), locus (locus),
53 : 74855 : try_result (CoercionResult::get_error ()), emit_errors (emit_errors),
54 : 74855 : try_flag (try_flag), is_cast_site (is_cast_site)
55 : 74855 : {}
56 : :
57 : : bool
58 : 74855 : 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 : 74855 : if (receiver->get_kind () == TyTy::TypeKind::NEVER)
65 : : {
66 : 429 : try_result = coerce_never (receiver);
67 : 429 : return true;
68 : : }
69 : :
70 : : // unsize
71 : 74426 : tl::expected<CoercionResult, CoerceUnsizedError> unsize_coercion
72 : 74426 : = coerce_unsized (receiver, expected);
73 : 74426 : if (unsize_coercion)
74 : : {
75 : 134 : try_result = unsize_coercion.value ();
76 : 134 : return true;
77 : : }
78 : 74292 : 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 : 74119 : switch (expected->get_kind ())
88 : : {
89 : 7509 : case TyTy::TypeKind::POINTER:
90 : 7509 : {
91 : 7509 : auto *ptr = expected->as<TyTy::PointerType> ();
92 : 7509 : try_result = coerce_unsafe_ptr (receiver, ptr, ptr->mutability ());
93 : 7509 : return !try_result.is_error ();
94 : : }
95 : :
96 : 23019 : case TyTy::TypeKind::REF:
97 : 23019 : {
98 : 23019 : auto *ptr = expected->as<TyTy::ReferenceType> ();
99 : 23019 : try_result
100 : 23019 : = coerce_borrowed_pointer (receiver, ptr, ptr->mutability ());
101 : 23019 : return !try_result.is_error ();
102 : : }
103 : 43591 : break;
104 : :
105 : 43591 : default:
106 : 43591 : break;
107 : : }
108 : :
109 : : // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L210
110 : 43591 : switch (receiver->get_kind ())
111 : : {
112 : 43591 : default:
113 : 43591 : {
114 : 43591 : 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 : 43591 : TyTy::BaseType *result
118 : 43591 : = unify_site_and (receiver->get_ref (),
119 : 43591 : TyTy::TyWithLocation (expected),
120 : 43591 : TyTy::TyWithLocation (receiver),
121 : : locus /*unify_locus*/, false /*emit_errors*/,
122 : 43591 : !try_flag /*commit_if_ok*/, try_flag /*infer*/,
123 : 43591 : try_flag /*cleanup on error*/);
124 : 43591 : if (result->get_kind () != TyTy::TypeKind::ERROR)
125 : : {
126 : 33599 : try_result = CoercionResult{{}, result};
127 : 33599 : return true;
128 : : }
129 : : }
130 : 9992 : break;
131 : : }
132 : :
133 : 9992 : return !try_result.is_error ();
134 : 74426 : }
135 : :
136 : : TypeCoercionRules::CoercionResult
137 : 429 : 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 : 429 : 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 : 428 : bool expected_is_infer_var = expected->get_kind () == TyTy::TypeKind::INFER;
157 : 428 : bool expected_is_general_infer_var
158 : : = expected_is_infer_var
159 : 428 : && (static_cast<TyTy::InferType *> (expected)->get_infer_kind ()
160 : 428 : == 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 : 428 : if (expected_is_general_infer_var)
169 : 1 : return CoercionResult{{}, receiver};
170 : : else
171 : 427 : return CoercionResult{{}, expected->clone ()};
172 : : }
173 : :
174 : : TypeCoercionRules::CoercionResult
175 : 7509 : TypeCoercionRules::coerce_unsafe_ptr (TyTy::BaseType *receiver,
176 : : TyTy::PointerType *expected,
177 : : Mutability to_mutbl)
178 : : {
179 : 7509 : rust_debug ("coerce_unsafe_ptr(receiver={%s}, expected={%s})",
180 : : receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
181 : :
182 : 7509 : Mutability from_mutbl = Mutability::Imm;
183 : 7509 : TyTy::BaseType *element = nullptr;
184 : 7509 : switch (receiver->get_kind ())
185 : : {
186 : 4000 : case TyTy::TypeKind::REF:
187 : 4000 : {
188 : 4000 : TyTy::ReferenceType *ref
189 : : = static_cast<TyTy::ReferenceType *> (receiver);
190 : 4000 : from_mutbl = ref->mutability ();
191 : 4000 : element = ref->get_base ();
192 : : }
193 : 4000 : break;
194 : :
195 : 3369 : case TyTy::TypeKind::POINTER:
196 : 3369 : {
197 : 3369 : TyTy::PointerType *ref = static_cast<TyTy::PointerType *> (receiver);
198 : 3369 : from_mutbl = ref->mutability ();
199 : 3369 : element = ref->get_base ();
200 : : }
201 : 3369 : 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 : 7369 : bool receiver_is_non_ptr = receiver->get_kind () != TyTy::TypeKind::POINTER;
215 : 7369 : 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 : 7355 : 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 : 7355 : TyTy::PointerType *coerced_mutability
230 : : = new TyTy::PointerType (receiver->get_ref (),
231 : 7355 : TyTy::TyVar (element->get_ref ()), to_mutbl);
232 : :
233 : 7355 : 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 : 7355 : TyTy::BaseType *result
245 : 7355 : = unify_site_and (receiver->get_ref (), TyTy::TyWithLocation (expected),
246 : 7355 : TyTy::TyWithLocation (coerced_mutability),
247 : : locus /*unify_locus*/, !try_flag /*emit_errors*/,
248 : 7355 : !try_flag /*commit_if_ok*/,
249 : 2179 : try_flag && !is_cast_site /*infer*/,
250 : 7355 : try_flag /*cleanup on error*/);
251 : 7355 : bool unsafe_ptr_coerceion_ok = result->get_kind () != TyTy::TypeKind::ERROR;
252 : 7355 : if (unsafe_ptr_coerceion_ok)
253 : 7292 : 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 : 23019 : TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver,
263 : : TyTy::ReferenceType *expected,
264 : : Mutability to_mutbl)
265 : : {
266 : 23019 : rust_debug ("coerce_borrowed_pointer(a={%s}, b={%s})",
267 : : receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
268 : :
269 : 23019 : Mutability from_mutbl = Mutability::Imm;
270 : 23019 : switch (receiver->get_kind ())
271 : : {
272 : 10726 : case TyTy::TypeKind::REF:
273 : 10726 : {
274 : 10726 : from_mutbl = receiver->as<TyTy::ReferenceType> ()->mutability ();
275 : : }
276 : 10726 : 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 : 10726 : 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 : 10726 : rust_debug ("coerce_borrowed_pointer -- autoderef cycle");
305 : 10726 : AutoderefCycle::cycle (receiver);
306 : 18578 : rust_debug ("coerce_borrowed_pointer -- result: [%s] with adjustments: [%zu]",
307 : : try_result.is_error () ? "failed" : "matched",
308 : : try_result.adjustments.size ());
309 : :
310 : 10726 : 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 : 74426 : TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
319 : : TyTy::BaseType *target)
320 : : {
321 : 74426 : rust_debug ("coerce_unsized(source={%s}, target={%s})",
322 : : source->debug_str ().c_str (), target->debug_str ().c_str ());
323 : :
324 : 74426 : bool source_is_ref = source->get_kind () == TyTy::TypeKind::REF;
325 : 74426 : bool target_is_ref = target->get_kind () == TyTy::TypeKind::REF;
326 : 74426 : bool target_is_ptr = target->get_kind () == TyTy::TypeKind::POINTER;
327 : :
328 : 74426 : bool needs_reborrow = false;
329 : 74426 : TyTy::BaseType *ty_a = source;
330 : 74426 : TyTy::BaseType *ty_b = target;
331 : 74426 : Mutability expected_mutability = Mutability::Imm;
332 : 74426 : if (source_is_ref && target_is_ref)
333 : : {
334 : 11032 : TyTy::ReferenceType *source_ref
335 : : = static_cast<TyTy::ReferenceType *> (source);
336 : 11032 : TyTy::ReferenceType *target_ref
337 : : = static_cast<TyTy::ReferenceType *> (target);
338 : :
339 : 11032 : Mutability from_mutbl = source_ref->mutability ();
340 : 11032 : Mutability to_mutbl = target_ref->mutability ();
341 : 11032 : 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 : 10860 : ty_a = source_ref->get_base ();
351 : 10860 : ty_b = target_ref->get_base ();
352 : 10860 : needs_reborrow = true;
353 : 10860 : expected_mutability = to_mutbl;
354 : :
355 : 10860 : adjustments.emplace_back (Adjustment::AdjustmentType::INDIRECTION,
356 : : source_ref, ty_a);
357 : : }
358 : 63394 : else if (source_is_ref && target_is_ptr)
359 : : {
360 : 4000 : TyTy::ReferenceType *source_ref
361 : : = static_cast<TyTy::ReferenceType *> (source);
362 : 4000 : TyTy::PointerType *target_ref = static_cast<TyTy::PointerType *> (target);
363 : :
364 : 4000 : Mutability from_mutbl = source_ref->mutability ();
365 : 4000 : Mutability to_mutbl = target_ref->mutability ();
366 : 4000 : 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 : 4000 : ty_a = source_ref->get_base ();
376 : 4000 : ty_b = target_ref->get_base ();
377 : 4000 : needs_reborrow = true;
378 : 4000 : expected_mutability = to_mutbl;
379 : :
380 : 4000 : 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 : 74254 : const auto a = ty_a;
391 : 74254 : const auto b = ty_b;
392 : :
393 : 74254 : bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC;
394 : 74254 : bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC;
395 : :
396 : 74254 : if (expect_dyn && need_unsize)
397 : : {
398 : 135 : bool bounds_compatible = b->bounds_compatible (*a, locus, false);
399 : 135 : if (!bounds_compatible)
400 : 1 : return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Unsafe);
401 : :
402 : : // return the unsize coercion
403 : 134 : TyTy::BaseType *result = b->clone ();
404 : : // result->set_ref (a->get_ref ());
405 : :
406 : : // append a dyn coercion adjustment
407 : 134 : adjustments.emplace_back (Adjustment::UNSIZE, a, result);
408 : :
409 : : // reborrow if needed
410 : 134 : 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 : 134 : return CoercionResult{adjustments, result};
425 : : }
426 : :
427 : 74119 : adjustments.clear ();
428 : 74119 : return tl::unexpected<CoerceUnsizedError> (CoerceUnsizedError::Regular);
429 : : }
430 : :
431 : : bool
432 : 17042 : TypeCoercionRules::select (TyTy::BaseType &autoderefed)
433 : : {
434 : 17042 : rust_debug ("TypeCoercionRules::select autoderefed={%s} can_eq expected={%s}",
435 : : autoderefed.debug_str ().c_str (),
436 : : expected->debug_str ().c_str ());
437 : :
438 : 17042 : TyTy::BaseType *result
439 : 17042 : = unify_site_and (autoderefed.get_ref (), TyTy::TyWithLocation (expected),
440 : 17042 : TyTy::TyWithLocation (&autoderefed),
441 : : UNDEF_LOCATION /* locus */, false /*emit_errors*/,
442 : 17042 : !try_flag /*commit_if_ok*/, try_flag /*infer*/,
443 : 17042 : try_flag /*cleanup*/);
444 : 17042 : bool ok = result->get_kind () != TyTy::TypeKind::ERROR;
445 : 17042 : if (!ok)
446 : : return false;
447 : :
448 : 7852 : try_result = CoercionResult{adjustments, result};
449 : 7852 : 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 : 33113 : TypeCoercionRules::coerceable_mutability (Mutability from_mutbl,
456 : : Mutability to_mutbl)
457 : : {
458 : 33113 : 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
|