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
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
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"
25 TypeCoercionRules::CoercionResult
26 TypeCoercionRules::Coerce (TyTy::BaseType
*receiver
, TyTy::BaseType
*expected
,
27 location_t locus
, bool allow_autoderef
,
30 TypeCoercionRules
resolver (expected
, locus
, true, allow_autoderef
, false,
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,
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
,
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
)
58 TypeCoercionRules::do_coercion (TyTy::BaseType
*receiver
)
60 // FIXME this is not finished and might be super simplified
62 // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs
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 ()};
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
};
101 try_result
= CoercionResult
{{}, expected
->clone ()};
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
;
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);
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
);
138 = coerce_borrowed_pointer (receiver
, ptr
, ptr
->mutability ());
139 return !try_result
.is_error ();
147 // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L210
148 switch (receiver
->get_kind ())
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
};
170 return !try_result
.is_error ();
173 TypeCoercionRules::CoercionResult
174 TypeCoercionRules::coerce_unsafe_ptr (TyTy::BaseType
*receiver
,
175 TyTy::PointerType
*expected
,
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 ();
193 case TyTy::TypeKind::POINTER
: {
194 TyTy::PointerType
*ref
= static_cast<TyTy::PointerType
*> (receiver
);
195 from_mutbl
= ref
->mutability ();
196 element
= ref
->get_base ();
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
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
,
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 ();
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 ());
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
))
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
))
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
));
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
386 // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L582
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
)
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
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
));
425 return CoercionResult
{adjustments
, result
};
428 adjustments
.clear ();
429 return TypeCoercionRules::CoercionResult::get_error ();
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
;
448 try_result
= CoercionResult
{adjustments
, result
};
452 /// Coercing a mutable reference to an immutable works, while
453 /// coercing `&T` to `&mut T` should be forbidden.
455 TypeCoercionRules::coerceable_mutability (Mutability from_mutbl
,
458 return to_mutbl
== Mutability::Imm
|| (from_mutbl
== to_mutbl
);
462 TypeCoercionRules::mismatched_mutability_error (location_t expr_locus
,
463 location_t lhs
, location_t rhs
)
468 rich_location
r (line_table
, expr_locus
);
471 rust_error_at (r
, "mismatched mutability");
475 TypeCoercionRules::object_unsafe_error (location_t expr_locus
, location_t lhs
,
481 rich_location
r (line_table
, expr_locus
);
484 rust_error_at (r
, "unsafe unsize coercion");
487 } // namespace Resolver