[PR testsuite/116860] Testsuite adjustment for recently added tests
[official-gcc.git] / gcc / rust / typecheck / rust-coercion.cc
blobdff1a04cf40bbaef611414637e2c538b2a8aaa2d
1 // Copyright (C) 2020-2025 Free Software Foundation, Inc.
3 // This file is part of GCC.
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.
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.
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/>.
19 #include "rust-coercion.h"
20 #include "rust-type-util.h"
22 namespace Rust {
23 namespace Resolver {
25 TypeCoercionRules::CoercionResult
26 TypeCoercionRules::Coerce (TyTy::BaseType *receiver, TyTy::BaseType *expected,
27 location_t locus, bool allow_autoderef,
28 bool is_cast_site)
30 TypeCoercionRules resolver (expected, locus, true, allow_autoderef, false,
31 is_cast_site);
32 bool ok = resolver.do_coercion (receiver);
33 return ok ? resolver.try_result : CoercionResult::get_error ();
36 TypeCoercionRules::CoercionResult
37 TypeCoercionRules::TryCoerce (TyTy::BaseType *receiver,
38 TyTy::BaseType *expected, location_t locus,
39 bool allow_autoderef, bool is_cast_site)
41 TypeCoercionRules resolver (expected, locus, false, allow_autoderef, true,
42 is_cast_site);
43 bool ok = resolver.do_coercion (receiver);
44 return ok ? resolver.try_result : CoercionResult::get_error ();
47 TypeCoercionRules::TypeCoercionRules (TyTy::BaseType *expected,
48 location_t locus, bool emit_errors,
49 bool allow_autoderef, bool try_flag,
50 bool is_cast_site)
51 : AutoderefCycle (!allow_autoderef), mappings (Analysis::Mappings::get ()),
52 context (TypeCheckContext::get ()), expected (expected), locus (locus),
53 try_result (CoercionResult::get_error ()), emit_errors (emit_errors),
54 try_flag (try_flag), is_cast_site (is_cast_site)
57 bool
58 TypeCoercionRules::do_coercion (TyTy::BaseType *receiver)
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
64 // handle never
65 // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L155
66 if (receiver->get_kind () == TyTy::TypeKind::NEVER)
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:
72 // let _: Option<?T> = Some({ return; });
74 // here, we would coerce from `!` to `?T`.
75 if (expected->has_substitutions_defined () && !expected->is_concrete ())
77 location_t locus = mappings->lookup_location (receiver->get_ref ());
78 TyTy::TyVar implicit_var
79 = TyTy::TyVar::get_implicit_infer_var (locus);
80 try_result = CoercionResult{{}, implicit_var.get_tyty ()};
81 return true;
83 else
85 bool expected_is_infer_var
86 = expected->get_kind () == TyTy::TypeKind::INFER;
87 bool expected_is_general_infer_var
88 = expected_is_infer_var
89 && (static_cast<TyTy::InferType *> (expected)->get_infer_kind ()
90 == TyTy::InferType::InferTypeKind::GENERAL);
92 // FIXME this 'expected_is_general_infer_var' case needs to eventually
93 // should go away see: compile/never_type_err1.rs
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 if (expected_is_general_infer_var)
99 try_result = CoercionResult{{}, receiver};
100 else
101 try_result = CoercionResult{{}, expected->clone ()};
103 return true;
107 // unsize
108 bool unsafe_error = false;
109 CoercionResult unsize_coercion
110 = coerce_unsized (receiver, expected, unsafe_error);
111 bool valid_unsize_coercion = !unsize_coercion.is_error ();
112 if (valid_unsize_coercion)
114 try_result = unsize_coercion;
115 return true;
117 else if (unsafe_error)
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;
125 // pointers
126 switch (expected->get_kind ())
128 case TyTy::TypeKind::POINTER: {
129 TyTy::PointerType *ptr = static_cast<TyTy::PointerType *> (expected);
130 try_result = coerce_unsafe_ptr (receiver, ptr, ptr->mutability ());
131 return !try_result.is_error ();
134 case TyTy::TypeKind::REF: {
135 TyTy::ReferenceType *ptr
136 = static_cast<TyTy::ReferenceType *> (expected);
137 try_result
138 = coerce_borrowed_pointer (receiver, ptr, ptr->mutability ());
139 return !try_result.is_error ();
141 break;
143 default:
144 break;
147 // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L210
148 switch (receiver->get_kind ())
150 default: {
151 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 TyTy::BaseType *result
155 = unify_site_and (receiver->get_ref (),
156 TyTy::TyWithLocation (expected),
157 TyTy::TyWithLocation (receiver),
158 locus /*unify_locus*/, false /*emit_errors*/,
159 !try_flag /*commit_if_ok*/, try_flag /*infer*/,
160 try_flag /*cleanup on error*/);
161 if (result->get_kind () != TyTy::TypeKind::ERROR)
163 try_result = CoercionResult{{}, result};
164 return true;
167 break;
170 return !try_result.is_error ();
173 TypeCoercionRules::CoercionResult
174 TypeCoercionRules::coerce_unsafe_ptr (TyTy::BaseType *receiver,
175 TyTy::PointerType *expected,
176 Mutability to_mutbl)
178 rust_debug ("coerce_unsafe_ptr(receiver={%s}, expected={%s})",
179 receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
181 Mutability from_mutbl = Mutability::Imm;
182 TyTy::BaseType *element = nullptr;
183 switch (receiver->get_kind ())
185 case TyTy::TypeKind::REF: {
186 TyTy::ReferenceType *ref
187 = static_cast<TyTy::ReferenceType *> (receiver);
188 from_mutbl = ref->mutability ();
189 element = ref->get_base ();
191 break;
193 case TyTy::TypeKind::POINTER: {
194 TyTy::PointerType *ref = static_cast<TyTy::PointerType *> (receiver);
195 from_mutbl = ref->mutability ();
196 element = ref->get_base ();
198 break;
200 default: {
201 // FIXME this can probably turn into a unify_and
202 if (receiver->can_eq (expected, false))
203 return CoercionResult{{}, expected->clone ()};
205 return CoercionResult::get_error ();
209 bool receiver_is_non_ptr = receiver->get_kind () != TyTy::TypeKind::POINTER;
210 if (autoderef_flag && receiver_is_non_ptr)
212 // it is unsafe to autoderef to raw pointers
213 return CoercionResult::get_error ();
216 if (!coerceable_mutability (from_mutbl, to_mutbl))
218 location_t lhs = mappings->lookup_location (receiver->get_ref ());
219 location_t rhs = mappings->lookup_location (expected->get_ref ());
220 mismatched_mutability_error (locus, lhs, rhs);
221 return TypeCoercionRules::CoercionResult::get_error ();
224 TyTy::PointerType *coerced_mutability
225 = new TyTy::PointerType (receiver->get_ref (),
226 TyTy::TyVar (element->get_ref ()), to_mutbl);
228 rust_debug ("coerce_unsafe_ptr unify-site");
230 // this is a really annoying case rust allows casts of any ptr to another ptr
231 // types
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
239 TyTy::BaseType *result
240 = unify_site_and (receiver->get_ref (), TyTy::TyWithLocation (expected),
241 TyTy::TyWithLocation (coerced_mutability),
242 locus /*unify_locus*/, !try_flag /*emit_errors*/,
243 !try_flag /*commit_if_ok*/,
244 try_flag && !is_cast_site /*infer*/,
245 try_flag /*cleanup on error*/);
246 bool unsafe_ptr_coerceion_ok = result->get_kind () != TyTy::TypeKind::ERROR;
247 if (unsafe_ptr_coerceion_ok)
248 return CoercionResult{{}, result};
250 return TypeCoercionRules::CoercionResult::get_error ();
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 TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver,
258 TyTy::ReferenceType *expected,
259 Mutability to_mutbl)
261 rust_debug ("coerce_borrowed_pointer(a={%s}, b={%s})",
262 receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
264 Mutability from_mutbl = Mutability::Imm;
265 switch (receiver->get_kind ())
267 case TyTy::TypeKind::REF: {
268 TyTy::ReferenceType *from
269 = static_cast<TyTy::ReferenceType *> (receiver);
270 from_mutbl = from->mutability ();
272 break;
274 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 rust_debug ("coerce_borrowed_pointer -- unify");
279 TyTy::BaseType *result
280 = unify_site_and (receiver->get_ref (),
281 TyTy::TyWithLocation (receiver),
282 TyTy::TyWithLocation (expected), locus,
283 false /*emit_errors*/, !try_flag /*commit_if_ok*/,
284 try_flag /* infer */,
285 try_flag /*cleanup_on_failure*/);
286 bool default_coerceion_ok
287 = result->get_kind () != TyTy::TypeKind::ERROR;
288 if (default_coerceion_ok)
289 return CoercionResult{{}, result};
291 return TypeCoercionRules::CoercionResult::get_error ();
295 if (!coerceable_mutability (from_mutbl, to_mutbl))
297 location_t lhs = mappings->lookup_location (receiver->get_ref ());
298 location_t rhs = mappings->lookup_location (expected->get_ref ());
299 mismatched_mutability_error (locus, lhs, rhs);
300 return TypeCoercionRules::CoercionResult::get_error ();
303 rust_debug ("coerce_borrowed_pointer -- autoderef cycle");
304 AutoderefCycle::cycle (receiver);
305 rust_debug ("coerce_borrowed_pointer -- result: [%s] with adjustments: [%zu]",
306 try_result.is_error () ? "failed" : "matched",
307 try_result.adjustments.size ());
309 return try_result;
312 // &[T; n] or &mut [T; n] -> &[T]
313 // or &mut [T; n] -> &mut [T]
314 // or &Concrete -> &Trait, etc.
315 TypeCoercionRules::CoercionResult
316 TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
317 TyTy::BaseType *target, bool &unsafe_error)
319 rust_debug ("coerce_unsized(source={%s}, target={%s})",
320 source->debug_str ().c_str (), target->debug_str ().c_str ());
322 bool source_is_ref = source->get_kind () == TyTy::TypeKind::REF;
323 bool target_is_ref = target->get_kind () == TyTy::TypeKind::REF;
324 bool target_is_ptr = target->get_kind () == TyTy::TypeKind::POINTER;
326 bool needs_reborrow = false;
327 TyTy::BaseType *ty_a = source;
328 TyTy::BaseType *ty_b = target;
329 Mutability expected_mutability = Mutability::Imm;
330 if (source_is_ref && target_is_ref)
332 TyTy::ReferenceType *source_ref
333 = static_cast<TyTy::ReferenceType *> (source);
334 TyTy::ReferenceType *target_ref
335 = static_cast<TyTy::ReferenceType *> (target);
337 Mutability from_mutbl = source_ref->mutability ();
338 Mutability to_mutbl = target_ref->mutability ();
339 if (!coerceable_mutability (from_mutbl, to_mutbl))
341 unsafe_error = true;
342 location_t lhs = mappings->lookup_location (source->get_ref ());
343 location_t rhs = mappings->lookup_location (target->get_ref ());
344 mismatched_mutability_error (locus, lhs, rhs);
345 return TypeCoercionRules::CoercionResult::get_error ();
348 ty_a = source_ref->get_base ();
349 ty_b = target_ref->get_base ();
350 needs_reborrow = true;
351 expected_mutability = to_mutbl;
353 adjustments.push_back (
354 Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
356 else if (source_is_ref && target_is_ptr)
358 TyTy::ReferenceType *source_ref
359 = static_cast<TyTy::ReferenceType *> (source);
360 TyTy::PointerType *target_ref = static_cast<TyTy::PointerType *> (target);
362 Mutability from_mutbl = source_ref->mutability ();
363 Mutability to_mutbl = target_ref->mutability ();
364 if (!coerceable_mutability (from_mutbl, to_mutbl))
366 unsafe_error = true;
367 location_t lhs = mappings->lookup_location (source->get_ref ());
368 location_t rhs = mappings->lookup_location (target->get_ref ());
369 mismatched_mutability_error (locus, lhs, rhs);
370 return TypeCoercionRules::CoercionResult::get_error ();
373 ty_a = source_ref->get_base ();
374 ty_b = target_ref->get_base ();
375 needs_reborrow = true;
376 expected_mutability = to_mutbl;
378 adjustments.push_back (
379 Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
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
388 const auto a = ty_a;
389 const auto b = ty_b;
391 bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC;
392 bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC;
394 if (expect_dyn && need_unsize)
396 bool bounds_compatible = b->bounds_compatible (*a, locus, true);
397 if (!bounds_compatible)
399 unsafe_error = true;
400 return TypeCoercionRules::CoercionResult::get_error ();
403 // return the unsize coercion
404 TyTy::BaseType *result = b->clone ();
405 // result->set_ref (a->get_ref ());
407 // append a dyn coercion adjustment
408 adjustments.push_back (Adjustment (Adjustment::UNSIZE, a, result));
410 // reborrow if needed
411 if (needs_reborrow)
413 TyTy::ReferenceType *reborrow
414 = new TyTy::ReferenceType (source->get_ref (),
415 TyTy::TyVar (result->get_ref ()),
416 expected_mutability);
418 Adjustment::AdjustmentType borrow_type
419 = expected_mutability == Mutability::Imm ? Adjustment::IMM_REF
420 : Adjustment::MUT_REF;
421 adjustments.push_back (Adjustment (borrow_type, result, reborrow));
422 result = reborrow;
425 return CoercionResult{adjustments, result};
428 adjustments.clear ();
429 return TypeCoercionRules::CoercionResult::get_error ();
432 bool
433 TypeCoercionRules::select (TyTy::BaseType &autoderefed)
435 rust_debug ("TypeCoercionRules::select autoderefed={%s} can_eq expected={%s}",
436 autoderefed.debug_str ().c_str (),
437 expected->debug_str ().c_str ());
439 TyTy::BaseType *result
440 = unify_site_and (autoderefed.get_ref (), TyTy::TyWithLocation (expected),
441 TyTy::TyWithLocation (&autoderefed),
442 UNDEF_LOCATION /* locus */, false /*emit_errors*/,
443 false /*commit_if_ok*/, true /*infer*/, true /*cleanup*/);
444 bool ok = result->get_kind () != TyTy::TypeKind::ERROR;
445 if (!ok)
446 return false;
448 try_result = CoercionResult{adjustments, result};
449 return true;
452 /// Coercing a mutable reference to an immutable works, while
453 /// coercing `&T` to `&mut T` should be forbidden.
454 bool
455 TypeCoercionRules::coerceable_mutability (Mutability from_mutbl,
456 Mutability to_mutbl)
458 return to_mutbl == Mutability::Imm || (from_mutbl == to_mutbl);
461 void
462 TypeCoercionRules::mismatched_mutability_error (location_t expr_locus,
463 location_t lhs, location_t rhs)
465 if (!emit_errors)
466 return;
468 rich_location r (line_table, expr_locus);
469 r.add_range (lhs);
470 r.add_range (rhs);
471 rust_error_at (r, "mismatched mutability");
474 void
475 TypeCoercionRules::object_unsafe_error (location_t expr_locus, location_t lhs,
476 location_t rhs)
478 if (!emit_errors)
479 return;
481 rich_location r (line_table, expr_locus);
482 r.add_range (lhs);
483 r.add_range (rhs);
484 rust_error_at (r, "unsafe unsize coercion");
487 } // namespace Resolver
488 } // namespace Rust