Files
rust/compiler/rustc_hir_analysis/src/check/coercion.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1891 lines
79 KiB
Rust
Raw Normal View History

//! # Type Coercion
//!
//! Under certain circumstances we will coerce from one type to another,
2019-02-08 14:53:55 +01:00
//! for example by auto-borrowing. This occurs in situations where the
//! compiler has a firm 'expected type' that was supplied from the user,
//! and where the actual type is similar to that expected type in purpose
//! but not in representation (so actual subtyping is inappropriate).
//!
//! ## Reborrowing
//!
//! Note that if we are expecting a reference, we will *reborrow*
2019-02-08 14:53:55 +01:00
//! even if the argument provided was already a reference. This is
2020-05-30 19:43:04 +02:00
//! useful for freezing mut things (that is, when the expected type is &T
//! but you have &mut T) and also for avoiding the linearity
2019-02-08 14:53:55 +01:00
//! of mut things (when the expected is &mut T and you have &mut T). See
2020-05-30 19:43:04 +02:00
//! the various `src/test/ui/coerce/*.rs` tests for
//! examples of where this is useful.
//!
//! ## Subtle note
//!
//! When inferring the generic arguments of functions, the argument
2020-05-30 19:43:04 +02:00
//! order is relevant, which can lead to the following edge case:
//!
2022-04-15 15:04:34 -07:00
//! ```ignore (illustrative)
2020-05-30 19:43:04 +02:00
//! fn foo<T>(a: T, b: T) {
//! // ...
//! }
//!
2020-05-30 19:43:04 +02:00
//! foo(&7i32, &mut 7i32);
//! // This compiles, as we first infer `T` to be `&i32`,
//! // and then coerce `&mut 7i32` to `&7i32`.
//!
2020-05-30 19:43:04 +02:00
//! foo(&mut 7i32, &7i32);
//! // This does not compile, as we first infer `T` to be `&mut i32`
//! // and are then unable to coerce `&7i32` to `&mut i32`.
//! ```
2020-01-14 13:18:06 -08:00
use crate::astconv::AstConv;
2020-06-15 21:59:09 +01:00
use crate::check::FnCtxt;
use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
2020-03-29 17:19:48 +02:00
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::Expr;
2020-03-29 17:19:48 +02:00
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{Coercion, InferOk, InferResult};
use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt};
use rustc_middle::lint::in_external_macro;
2020-03-29 16:41:09 +02:00
use rustc_middle::ty::adjustment::{
2019-12-24 17:38:22 -05:00
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
};
2020-03-29 16:41:09 +02:00
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::relate::RelateResult;
use rustc_middle::ty::subst::SubstsRef;
2022-06-17 13:15:00 +01:00
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, ToPredicate, Ty, TypeAndMut};
use rustc_session::parse::feature_err;
2020-01-01 19:30:57 +01:00
use rustc_span::symbol::sym;
2021-08-19 16:57:15 -05:00
use rustc_span::{self, BytePos, DesugaringKind, Span};
2019-12-24 17:38:22 -05:00
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
2020-02-11 21:19:40 +01:00
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use smallvec::{smallvec, SmallVec};
use std::ops::Deref;
struct Coerce<'a, 'tcx> {
2019-06-14 00:48:52 +03:00
fcx: &'a FnCtxt<'a, 'tcx>,
cause: ObligationCause<'tcx>,
use_lub: bool,
/// Determines whether or not allow_two_phase_borrow is set on any
/// autoref adjustments we create while coercing. We don't want to
/// allow deref coercions to create two-phase borrows, at least initially,
/// but we do need two-phase borrows for function argument reborrows.
/// See #47489 and #48598
/// See docs on the "AllowTwoPhase" type for a more detailed discussion
allow_two_phase: AllowTwoPhase,
}
2019-06-14 00:48:52 +03:00
impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
type Target = FnCtxt<'a, 'tcx>;
fn deref(&self) -> &Self::Target {
&self.fcx
}
}
type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
struct CollectRetsVisitor<'tcx> {
ret_exprs: Vec<&'tcx hir::Expr<'tcx>>,
}
impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if let hir::ExprKind::Ret(_) = expr.kind {
self.ret_exprs.push(expr);
}
intravisit::walk_expr(self, expr);
}
}
2020-05-30 19:43:04 +02:00
/// Coercing a mutable reference to an immutable works, while
/// coercing `&T` to `&mut T` should be forbidden.
2019-12-24 17:38:22 -05:00
fn coerce_mutbls<'tcx>(
from_mutbl: hir::Mutability,
to_mutbl: hir::Mutability,
) -> RelateResult<'tcx, ()> {
match (from_mutbl, to_mutbl) {
(hir::Mutability::Mut, hir::Mutability::Mut | hir::Mutability::Not)
| (hir::Mutability::Not, hir::Mutability::Not) => Ok(()),
(hir::Mutability::Not, hir::Mutability::Mut) => Err(TypeError::Mutability),
}
}
2020-04-11 10:23:33 +02:00
/// Do not require any adjustments, i.e. coerce `x -> x`.
2019-12-24 17:38:22 -05:00
fn identity(_: Ty<'_>) -> Vec<Adjustment<'_>> {
vec![]
}
2021-12-13 21:45:08 -04:00
fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>> {
move |target| vec![Adjustment { kind, target }]
}
2020-04-11 10:23:33 +02:00
/// This always returns `Ok(...)`.
2019-12-24 17:38:22 -05:00
fn success<'tcx>(
adj: Vec<Adjustment<'tcx>>,
target: Ty<'tcx>,
obligations: traits::PredicateObligations<'tcx>,
) -> CoerceResult<'tcx> {
Ok(InferOk { value: (adj, target), obligations })
}
2019-06-14 00:48:52 +03:00
impl<'f, 'tcx> Coerce<'f, 'tcx> {
fn new(
fcx: &'f FnCtxt<'f, 'tcx>,
cause: ObligationCause<'tcx>,
allow_two_phase: AllowTwoPhase,
) -> Self {
2019-12-24 17:38:22 -05:00
Coerce { fcx, cause, allow_two_phase, use_lub: false }
}
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
2020-04-11 10:23:33 +02:00
debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
self.commit_if_ok(|_| {
if self.use_lub {
self.at(&self.cause, self.fcx.param_env).lub(b, a)
} else {
self.at(&self.cause, self.fcx.param_env)
.sup(b, a)
.map(|InferOk { value: (), obligations }| InferOk { value: a, obligations })
}
})
}
/// Unify two types (using sub or lub) and produce a specific coercion.
2019-12-24 17:38:22 -05:00
fn unify_and<F>(&self, a: Ty<'tcx>, b: Ty<'tcx>, f: F) -> CoerceResult<'tcx>
where
F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
{
2022-01-25 14:13:38 +11:00
self.unify(a, b)
2019-12-24 17:38:22 -05:00
.and_then(|InferOk { value: ty, obligations }| success(f(ty), ty, obligations))
}
#[instrument(skip(self))]
fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
// First, remove any resolved type variables (at the top level, at least):
let a = self.shallow_resolve(a);
let b = self.shallow_resolve(b);
debug!("Coerce.tys({:?} => {:?})", a, b);
// Just ignore error types.
if a.references_error() || b.references_error() {
return success(vec![], self.fcx.tcx.ty_error(), vec![]);
}
// Coercing from `!` to any type is allowed:
if a.is_never() {
return success(simple(Adjust::NeverToAny)(b), b, vec![]);
2016-06-25 20:02:09 +08:00
}
// Coercing *from* an unresolved inference variable means that
// we have no information about the source type. This will always
// ultimately fall back to some form of subtyping.
if a.is_ty_var() {
return self.coerce_from_inference_variable(a, b, identity);
}
DST coercions and DST structs [breaking-change] 1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code. 2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible. 3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 14:20:11 +02:00
// Consider coercing the subtype to a DST
//
// NOTE: this is wrapped in a `commit_if_ok` because it creates
// a "spurious" type variable, and we don't want to have that
// type variable in memory if the coercion fails.
let unsize = self.commit_if_ok(|_| self.coerce_unsized(a, b));
match unsize {
Ok(_) => {
debug!("coerce: unsize successful");
return unsize;
}
Err(TypeError::ObjectUnsafeCoercion(did)) => {
debug!("coerce: unsize not object safe");
return Err(TypeError::ObjectUnsafeCoercion(did));
}
Err(error) => {
debug!(?error, "coerce: unsize failed");
}
DST coercions and DST structs [breaking-change] 1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code. 2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible. 3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 14:20:11 +02:00
}
// Examine the supertype and consider auto-borrowing.
2020-08-03 00:49:11 +02:00
match *b.kind() {
ty::RawPtr(mt_b) => {
return self.coerce_unsafe_ptr(a, b, mt_b.mutbl);
}
ty::Ref(r_b, _, mutbl_b) => {
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
}
_ => {}
}
2020-08-03 00:49:11 +02:00
match *a.kind() {
ty::FnDef(..) => {
// Function items are coercible to any closure
// type; function pointers are not (that would
// require double indirection).
2016-10-24 17:33:41 -07:00
// Additionally, we permit coercion of function
// items to drop the unsafe qualifier.
self.coerce_from_fn_item(a, b)
}
ty::FnPtr(a_f) => {
// We permit coercion of fn pointers to drop the
// unsafe qualifier.
self.coerce_from_fn_pointer(a, a_f, b)
}
ty::Closure(closure_def_id_a, substs_a) => {
// Non-capturing closures are coercible to
2019-04-01 00:00:43 +09:00
// function pointers or unsafe function pointers.
// It cannot convert closures that require unsafe.
self.coerce_closure_to_fn(a, closure_def_id_a, substs_a, b)
}
_ => {
// Otherwise, just use unification rules.
self.unify_and(a, b, identity)
}
}
}
/// Coercing *from* an inference variable. In this case, we have no information
/// about the source type, so we can't really do a true coercion and we always
/// fall back to subtyping (`unify_and`).
fn coerce_from_inference_variable(
&self,
a: Ty<'tcx>,
b: Ty<'tcx>,
make_adjustments: impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
) -> CoerceResult<'tcx> {
debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b);
assert!(a.is_ty_var() && self.shallow_resolve(a) == a);
assert!(self.shallow_resolve(b) == b);
if b.is_ty_var() {
// Two unresolved type variables: create a `Coerce` predicate.
let target_ty = if self.use_lub {
self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::LatticeVariable,
span: self.cause.span,
})
} else {
b
};
let mut obligations = Vec::with_capacity(2);
for &source_ty in &[a, b] {
if source_ty != target_ty {
obligations.push(Obligation::new(
self.cause.clone(),
self.param_env,
ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate {
a: source_ty,
b: target_ty,
}))
.to_predicate(self.tcx()),
));
}
}
debug!(
"coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}",
target_ty, obligations
);
let adjustments = make_adjustments(target_ty);
InferResult::Ok(InferOk { value: (adjustments, target_ty), obligations })
} else {
// One unresolved type variable: just apply subtyping, we may be able
// to do something useful.
self.unify_and(a, b, make_adjustments)
}
}
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
/// To match `A` with `B`, autoderef will be performed,
/// calling `deref`/`deref_mut` where necessary.
2019-12-24 17:38:22 -05:00
fn coerce_borrowed_pointer(
&self,
a: Ty<'tcx>,
b: Ty<'tcx>,
r_b: ty::Region<'tcx>,
mutbl_b: hir::Mutability,
2019-12-24 17:38:22 -05:00
) -> CoerceResult<'tcx> {
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
// If we have a parameter of type `&M T_a` and the value
// provided is `expr`, we will be adding an implicit borrow,
// meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
// to type check, we will construct the type that `&M*expr` would
// yield.
2020-08-03 00:49:11 +02:00
let (r_a, mt_a) = match *a.kind() {
ty::Ref(r_a, ty, mutbl) => {
let mt_a = ty::TypeAndMut { ty, mutbl };
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
2016-03-18 17:19:52 -04:00
(r_a, mt_a)
2015-01-20 22:56:53 +09:00
}
_ => return self.unify_and(a, b, identity),
};
let span = self.cause.span;
let mut first_error = None;
let mut r_borrow_var = None;
let mut autoderef = self.autoderef(span, a);
let mut found = None;
for (referent_ty, autoderefs) in autoderef.by_ref() {
if autoderefs == 0 {
// Don't let this pass, otherwise it would cause
// &T to autoref to &&T.
continue;
}
// At this point, we have deref'd `a` to `referent_ty`. So
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
// In the autoderef loop for `&'a mut Vec<T>`, we would get
// three callbacks:
//
// - `&'a mut Vec<T>` -- 0 derefs, just ignore it
// - `Vec<T>` -- 1 deref
// - `[T]` -- 2 deref
//
// At each point after the first callback, we want to
// check to see whether this would match out target type
// (`&'b mut [T]`) if we autoref'd it. We can't just
// compare the referent types, though, because we still
// have to consider the mutability. E.g., in the case
// we've been considering, we have an `&mut` reference, so
// the `T` in `[T]` needs to be unified with equality.
//
// Therefore, we construct reference types reflecting what
// the types will be after we do the final auto-ref and
// compare those. Note that this means we use the target
// mutability [1], since it may be that we are coercing
// from `&mut T` to `&U`.
//
// One fine point concerns the region that we use. We
// choose the region such that the region of the final
// type that results from `unify` will be the region we
// want for the autoref:
//
// - if in sub mode, that means we want to use `'b` (the
// region from the target reference) for both
// pointers [2]. This is because sub mode (somewhat
// arbitrarily) returns the subtype region. In the case
// where we are coercing to a target type, we know we
// want to use that target type region (`'b`) because --
// for the program to type-check -- it must be the
// smaller of the two.
// - One fine point. It may be surprising that we can
// use `'b` without relating `'a` and `'b`. The reason
// that this is ok is that what we produce is
// effectively a `&'b *x` expression (if you could
// annotate the region of a borrow), and regionck has
// code that adds edges from the region of a borrow
// (`'b`, here) into the regions in the borrowed
// expression (`*x`, here). (Search for "link".)
// - if in lub mode, things can get fairly complicated. The
// easiest thing is just to make a fresh
// region variable [4], which effectively means we defer
// the decision to region inference (and regionck, which will add
// some more edges to this variable). However, this can wind up
// creating a crippling number of variables in some cases --
// e.g., #32278 -- so we optimize one particular case [3].
// Let me try to explain with some examples:
// - The "running example" above represents the simple case,
// where we have one `&` reference at the outer level and
// ownership all the rest of the way down. In this case,
// we want `LUB('a, 'b)` as the resulting region.
// - However, if there are nested borrows, that region is
// too strong. Consider a coercion from `&'a &'x Rc<T>` to
// `&'b T`. In this case, `'a` is actually irrelevant.
// The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)`
2019-07-27 02:26:27 +03:00
// we get spurious errors (`ui/regions-lub-ref-ref-rc.rs`).
// (The errors actually show up in borrowck, typically, because
// this extra edge causes the region `'a` to be inferred to something
// too big, which then results in borrowck errors.)
// - We could track the innermost shared reference, but there is already
// code in regionck that has the job of creating links between
// the region of a borrow and the regions in the thing being
// borrowed (here, `'a` and `'x`), and it knows how to handle
// all the various cases. So instead we just make a region variable
// and let regionck figure it out.
let r = if !self.use_lub {
r_b // [2] above
} else if autoderefs == 1 {
r_a // [3] above
} else {
if r_borrow_var.is_none() {
// create var lazily, at most once
let coercion = Coercion(span);
let r = self.next_region_var(coercion);
r_borrow_var = Some(r); // [4] above
}
r_borrow_var.unwrap()
};
2019-12-24 17:38:22 -05:00
let derefd_ty_a = self.tcx.mk_ref(
r,
TypeAndMut {
ty: referent_ty,
mutbl: mutbl_b, // [1] above
2019-12-24 17:38:22 -05:00
},
);
match self.unify(derefd_ty_a, b) {
Ok(ok) => {
found = Some(ok);
break;
}
Err(err) => {
if first_error.is_none() {
first_error = Some(err);
}
}
}
}
// Extract type or return an error. We return the first error
// we got, which should be from relating the "base" type
// (e.g., in example above, the failure from relating `Vec<T>`
// to the target type), since that should be the least
// confusing.
let Some(InferOk { value: ty, mut obligations }) = found else {
let err = first_error.expect("coerce_borrowed_pointer had no error");
debug!("coerce_borrowed_pointer: failed with err = {:?}", err);
return Err(err);
};
if ty == a && mt_a.mutbl == hir::Mutability::Not && autoderef.step_count() == 1 {
2016-03-18 17:19:52 -04:00
// As a special case, if we would produce `&'a *x`, that's
// a total no-op. We end up with the type `&'a T` just as
// we started with. In that case, just skip it
// altogether. This is just an optimization.
//
// Note that for `&mut`, we DO want to reborrow --
// otherwise, this would be a move, which might be an
// error. For example `foo(self.x)` where `self` and
// `self.x` both have `&mut `type would be a move of
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
// which is a borrow.
assert_eq!(mutbl_b, hir::Mutability::Not); // can only coerce &T -> &U
return success(vec![], ty, obligations);
2016-03-18 17:19:52 -04:00
}
2019-12-24 17:38:22 -05:00
let InferOk { value: mut adjustments, obligations: o } =
self.adjust_steps_as_infer_ok(&autoderef);
obligations.extend(o);
obligations.extend(autoderef.into_obligations());
// Now apply the autoref. We have to extract the region out of
// the final ref type we got.
let ty::Ref(r_borrow, _, _) = ty.kind() else {
span_bug!(span, "expected a ref type, got {:?}", ty);
};
let mutbl = match mutbl_b {
hir::Mutability::Not => AutoBorrowMutability::Not,
2019-12-24 17:38:22 -05:00
hir::Mutability::Mut => {
AutoBorrowMutability::Mut { allow_two_phase_borrow: self.allow_two_phase }
}
};
adjustments.push(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(*r_borrow, mutbl)),
2019-12-24 17:38:22 -05:00
target: ty,
});
2019-12-24 17:38:22 -05:00
debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments);
success(adjustments, ty, obligations)
}
// &[T; n] or &mut [T; n] -> &[T]
// or &mut [T; n] -> &mut [T]
DST coercions and DST structs [breaking-change] 1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code. 2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible. 3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 14:20:11 +02:00
// or &Concrete -> &Trait, etc.
2021-08-20 13:36:04 +00:00
#[instrument(skip(self), level = "debug")]
fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> {
source = self.shallow_resolve(source);
target = self.shallow_resolve(target);
2021-08-20 13:36:04 +00:00
debug!(?source, ?target);
// These 'if' statements require some explanation.
// The `CoerceUnsized` trait is special - it is only
// possible to write `impl CoerceUnsized<B> for A` where
// A and B have 'matching' fields. This rules out the following
// two types of blanket impls:
//
// `impl<T> CoerceUnsized<T> for SomeType`
// `impl<T> CoerceUnsized<SomeType> for T`
//
// Both of these trigger a special `CoerceUnsized`-related error (E0376)
//
2020-08-02 23:20:00 +08:00
// We can take advantage of this fact to avoid performing unnecessary work.
// If either `source` or `target` is a type variable, then any applicable impl
// would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`)
// or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for
// SomeType`).
//
// However, these are exactly the kinds of impls which are forbidden by
// the compiler! Therefore, we can be sure that coercion will always fail
// when either the source or target type is a type variable. This allows us
// to skip performing any trait selection, and immediately bail out.
if source.is_ty_var() {
debug!("coerce_unsized: source is a TyVar, bailing out");
return Err(TypeError::Mismatch);
}
if target.is_ty_var() {
debug!("coerce_unsized: target is a TyVar, bailing out");
return Err(TypeError::Mismatch);
}
2019-12-24 17:38:22 -05:00
let traits =
(self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait());
let (Some(unsize_did), Some(coerce_unsized_did)) = traits else {
debug!("missing Unsize or CoerceUnsized traits");
2015-07-10 15:40:04 -07:00
return Err(TypeError::Mismatch);
};
DST coercions and DST structs [breaking-change] 1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code. 2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible. 3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 14:20:11 +02:00
// Note, we want to avoid unnecessary unsizing. We don't want to coerce to
// a DST unless we have to. This currently comes out in the wash since
// we can't unify [T] with U. But to properly support DST, we need to allow
// that, at which point we will need extra checks on the target here.
DST coercions and DST structs [breaking-change] 1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code. 2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible. 3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 14:20:11 +02:00
// Handle reborrows before selecting `Source: CoerceUnsized<Target>`.
2020-08-03 00:49:11 +02:00
let reborrow = match (source.kind(), target.kind()) {
(&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => {
coerce_mutbls(mutbl_a, mutbl_b)?;
let coercion = Coercion(self.cause.span);
let r_borrow = self.next_region_var(coercion);
let mutbl = match mutbl_b {
hir::Mutability::Not => AutoBorrowMutability::Not,
hir::Mutability::Mut => AutoBorrowMutability::Mut {
// We don't allow two-phase borrows here, at least for initial
// implementation. If it happens that this coercion is a function argument,
// the reborrow in coerce_borrowed_ptr will pick it up.
allow_two_phase_borrow: AllowTwoPhase::No,
2019-12-24 17:38:22 -05:00
},
};
2019-12-24 17:38:22 -05:00
Some((
Adjustment { kind: Adjust::Deref(None), target: ty_a },
Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)),
target: self
.tcx
.mk_ref(r_borrow, ty::TypeAndMut { mutbl: mutbl_b, ty: ty_a }),
},
))
}
(&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(ty::TypeAndMut { mutbl: mt_b, .. })) => {
coerce_mutbls(mt_a, mt_b)?;
2019-12-24 17:38:22 -05:00
Some((
Adjustment { kind: Adjust::Deref(None), target: ty_a },
Adjustment {
kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)),
target: self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mt_b, ty: ty_a }),
},
))
}
_ => None,
};
let coerce_source = reborrow.as_ref().map_or(source, |&(_, ref r)| r.target);
// Setup either a subtyping or a LUB relationship between
// the `CoerceUnsized` target type and the expected type.
// We only have the latter, so we use an inference variable
// for the former and let type inference do the rest.
let origin = TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span: self.cause.span,
};
let coerce_target = self.next_ty_var(origin);
let mut coercion = self.unify_and(coerce_target, target, |target| {
2019-12-24 17:38:22 -05:00
let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target };
match reborrow {
None => vec![unsize],
2019-12-24 17:38:22 -05:00
Some((ref deref, ref autoref)) => vec![deref.clone(), autoref.clone(), unsize],
}
})?;
let mut selcx = traits::SelectionContext::new(self);
// Create an obligation for `Source: CoerceUnsized<Target>`.
let cause = ObligationCause::new(
self.cause.span,
self.body_id,
ObligationCauseCode::Coercion { source, target },
);
// Use a FIFO queue for this custom fulfillment procedure.
//
// A Vec (or SmallVec) is not a natural choice for a queue. However,
// this code path is hot, and this queue usually has a max length of 1
// and almost never more than 3. By using a SmallVec we avoid an
// allocation, at the (very small) cost of (occasionally) having to
// shift subsequent elements down when removing the front element.
2020-01-05 20:27:00 +01:00
let mut queue: SmallVec<[_; 4]> = smallvec![traits::predicate_for_trait_def(
self.tcx,
2019-12-24 17:38:22 -05:00
self.fcx.param_env,
cause,
coerce_unsized_did,
0,
coerce_source,
&[coerce_target.into()]
)];
let mut has_unsized_tuple_coercion = false;
let mut has_trait_upcasting_coercion = None;
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
// inference might unify those two inner type variables later.
let traits = [coerce_unsized_did, unsize_did];
while !queue.is_empty() {
let obligation = queue.remove(0);
2015-06-18 20:25:05 +03:00
debug!("coerce_unsized resolve step: {:?}", obligation);
2021-01-07 11:20:28 -05:00
let bound_predicate = obligation.predicate.kind();
let trait_pred = match bound_predicate.skip_binder() {
2021-07-22 21:56:07 +08:00
ty::PredicateKind::Trait(trait_pred) if traits.contains(&trait_pred.def_id()) => {
2020-06-21 12:26:17 +02:00
if unsize_did == trait_pred.def_id() {
let self_ty = trait_pred.self_ty();
2020-06-21 12:26:17 +02:00
let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty();
if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) =
(self_ty.kind(), unsize_ty.kind())
&& data_a.principal_def_id() != data_b.principal_def_id()
{
debug!("coerce_unsized: found trait upcasting coercion");
has_trait_upcasting_coercion = Some((self_ty, unsize_ty));
}
2020-08-03 00:49:11 +02:00
if let ty::Tuple(..) = unsize_ty.kind() {
2020-06-21 12:26:17 +02:00
debug!("coerce_unsized: found unsized tuple coercion");
has_unsized_tuple_coercion = true;
}
}
2020-10-16 14:04:11 -04:00
bound_predicate.rebind(trait_pred)
2020-06-21 12:26:17 +02:00
}
_ => {
coercion.obligations.push(obligation);
continue;
}
};
match selcx.select(&obligation.with(trait_pred)) {
// Uncertain or unimplemented.
Ok(None) => {
if trait_pred.def_id() == unsize_did {
2020-10-24 02:21:18 +02:00
let trait_pred = self.resolve_vars_if_possible(trait_pred);
let self_ty = trait_pred.skip_binder().self_ty();
let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty();
debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
2020-08-03 00:49:11 +02:00
match (&self_ty.kind(), &unsize_ty.kind()) {
2019-12-24 17:38:22 -05:00
(ty::Infer(ty::TyVar(v)), ty::Dynamic(..))
if self.type_var_is_sized(*v) =>
{
debug!("coerce_unsized: have sized infer {:?}", v);
coercion.obligations.push(obligation);
// `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
// for unsizing.
}
_ => {
// Some other case for `$0: Unsize<Something>`. Note that we
// hit this case even if `Something` is a sized type, so just
// don't do the coercion.
debug!("coerce_unsized: ambiguous unsize");
return Err(TypeError::Mismatch);
}
}
} else {
debug!("coerce_unsized: early return - ambiguous");
return Err(TypeError::Mismatch);
}
}
Err(traits::Unimplemented) => {
2015-05-12 14:41:08 +12:00
debug!("coerce_unsized: early return - can't prove obligation");
2015-07-10 15:40:04 -07:00
return Err(TypeError::Mismatch);
2015-03-23 15:26:51 +13:00
}
// Object safety violations or miscellaneous.
Err(err) => {
self.err_ctxt().report_selection_error(
obligation.clone(),
&obligation,
&err,
false,
);
// Treat this like an obligation and follow through
// with the unsizing - the lack of a coercion should
// be silent, as it causes a type mismatch later.
}
2020-05-11 15:25:33 +00:00
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
DST coercions and DST structs [breaking-change] 1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code. 2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible. 3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 14:20:11 +02:00
}
}
2018-02-14 16:11:02 +01:00
if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
2020-01-02 12:42:42 +01:00
feature_err(
2019-11-30 02:40:28 +01:00
&self.tcx.sess.parse_sess,
sym::unsized_tuple_coercion,
self.cause.span,
"unsized tuple coercion is not stable enough for use and is subject to change",
)
.emit();
}
if let Some((sub, sup)) = has_trait_upcasting_coercion
&& !self.tcx().features().trait_upcasting
{
// Renders better when we erase regions, since they're not really the point here.
let (sub, sup) = self.tcx.erase_regions((sub, sup));
let mut err = feature_err(
&self.tcx.sess.parse_sess,
sym::trait_upcasting,
self.cause.span,
&format!("cannot cast `{sub}` to `{sup}`, trait upcasting coercion is experimental"),
);
err.note(&format!("required when coercing `{source}` into `{target}`"));
err.emit();
}
Ok(coercion)
DST coercions and DST structs [breaking-change] 1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code. 2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible. 3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 14:20:11 +02:00
}
2019-12-24 17:38:22 -05:00
fn coerce_from_safe_fn<F, G>(
&self,
a: Ty<'tcx>,
fn_ty_a: ty::PolyFnSig<'tcx>,
b: Ty<'tcx>,
to_unsafe: F,
normal: G,
) -> CoerceResult<'tcx>
where
F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
{
2022-07-20 13:34:53 +02:00
self.commit_if_ok(|snapshot| {
let result = if let ty::FnPtr(fn_ty_b) = b.kind()
&& let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
(fn_ty_a.unsafety(), fn_ty_b.unsafety())
{
let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
self.unify_and(unsafe_a, b, to_unsafe)
} else {
self.unify_and(a, b, normal)
};
// FIXME(#73154): This is a hack. Currently LUB can generate
// unsolvable constraints. Additionally, it returns `a`
// unconditionally, even when the "LUB" is `b`. In the future, we
// want the coerced type to be the actual supertype of these two,
// but for now, we want to just error to ensure we don't lock
// ourselves into a specific behavior with NLL.
self.leak_check(false, snapshot)?;
result
})
}
2019-12-24 17:38:22 -05:00
fn coerce_from_fn_pointer(
&self,
a: Ty<'tcx>,
fn_ty_a: ty::PolyFnSig<'tcx>,
b: Ty<'tcx>,
) -> CoerceResult<'tcx> {
//! Attempts to coerce from the type of a Rust function item
//! into a closure or a `proc`.
//!
let b = self.shallow_resolve(b);
debug!("coerce_from_fn_pointer(a={:?}, b={:?})", a, b);
2019-12-24 17:38:22 -05:00
self.coerce_from_safe_fn(
a,
fn_ty_a,
b,
simple(Adjust::Pointer(PointerCast::UnsafeFnPointer)),
identity,
)
}
2019-12-24 17:38:22 -05:00
fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
//! Attempts to coerce from the type of a Rust function item
//! into a closure or a `proc`.
let b = self.shallow_resolve(b);
let InferOk { value: b, mut obligations } =
self.normalize_associated_types_in_as_infer_ok(self.cause.span, b);
debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
2020-08-03 00:49:11 +02:00
match b.kind() {
ty::FnPtr(b_sig) => {
let a_sig = a.fn_sig(self.tcx);
if let ty::FnDef(def_id, _) = *a.kind() {
// Intrinsics are not coercible to function pointers
if self.tcx.is_intrinsic(def_id) {
return Err(TypeError::IntrinsicCast);
}
// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
if b_sig.unsafety() == hir::Unsafety::Normal
&& !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
{
return Err(TypeError::TargetFeatureCast(def_id));
}
}
let InferOk { value: a_sig, obligations: o1 } =
2020-10-24 02:21:18 +02:00
self.normalize_associated_types_in_as_infer_ok(self.cause.span, a_sig);
obligations.extend(o1);
let a_fn_pointer = self.tcx.mk_fn_ptr(a_sig);
let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn(
a_fn_pointer,
a_sig,
b,
|unsafe_ty| {
vec![
Adjustment {
kind: Adjust::Pointer(PointerCast::ReifyFnPointer),
2019-12-24 17:38:22 -05:00
target: a_fn_pointer,
},
Adjustment {
kind: Adjust::Pointer(PointerCast::UnsafeFnPointer),
2019-12-24 17:38:22 -05:00
target: unsafe_ty,
},
]
},
2019-12-24 17:38:22 -05:00
simple(Adjust::Pointer(PointerCast::ReifyFnPointer)),
)?;
obligations.extend(o2);
Ok(InferOk { value, obligations })
}
_ => self.unify_and(a, b, identity),
}
}
2019-12-24 17:38:22 -05:00
fn coerce_closure_to_fn(
&self,
a: Ty<'tcx>,
closure_def_id_a: DefId,
2019-12-24 17:38:22 -05:00
substs_a: SubstsRef<'tcx>,
b: Ty<'tcx>,
) -> CoerceResult<'tcx> {
//! Attempts to coerce from the type of a non-capturing closure
//! into a function pointer.
//!
let b = self.shallow_resolve(b);
2020-08-03 00:49:11 +02:00
match b.kind() {
// At this point we haven't done capture analysis, which means
// that the ClosureSubsts just contains an inference variable instead
// of tuple of captured types.
//
// All we care here is if any variable is being captured and not the exact paths,
// so we check `upvars_mentioned` for root variables being captured.
ty::FnPtr(fn_ty)
if self
.tcx
.upvars_mentioned(closure_def_id_a.expect_local())
.map_or(true, |u| u.is_empty()) =>
{
// We coerce the closure, which has fn type
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
// to
// `fn(arg0,arg1,...) -> _`
2019-04-01 00:00:43 +09:00
// or
// `unsafe fn(arg0,arg1,...) -> _`
let closure_sig = substs_a.as_closure().sig();
2019-04-01 00:00:43 +09:00
let unsafety = fn_ty.unsafety();
let pointer_ty =
self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety));
2019-12-24 17:38:22 -05:00
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
self.unify_and(
pointer_ty,
b,
simple(Adjust::Pointer(PointerCast::ClosureFnPointer(unsafety))),
)
}
_ => self.unify_and(a, b, identity),
}
}
2019-12-24 17:38:22 -05:00
fn coerce_unsafe_ptr(
&self,
a: Ty<'tcx>,
b: Ty<'tcx>,
mutbl_b: hir::Mutability,
) -> CoerceResult<'tcx> {
debug!("coerce_unsafe_ptr(a={:?}, b={:?})", a, b);
2020-08-03 00:49:11 +02:00
let (is_ref, mt_a) = match *a.kind() {
ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }),
ty::RawPtr(mt) => (false, mt),
2019-12-24 17:38:22 -05:00
_ => return self.unify_and(a, b, identity),
};
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
// Check that the types which they point at are compatible.
2019-12-24 17:38:22 -05:00
let a_unsafe = self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty });
// Although references and unsafe ptrs have the same
// representation, we still register an Adjust::DerefRef so that
// regionck knows that the region for `a` must be valid here.
if is_ref {
self.unify_and(a_unsafe, b, |target| {
2019-12-24 17:38:22 -05:00
vec![
Adjustment { kind: Adjust::Deref(None), target: mt_a.ty },
Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), target },
]
})
} else if mt_a.mutbl != mutbl_b {
2019-12-24 17:38:22 -05:00
self.unify_and(a_unsafe, b, simple(Adjust::Pointer(PointerCast::MutToConstPointer)))
} else {
self.unify_and(a_unsafe, b, identity)
}
}
}
2019-06-14 00:48:52 +03:00
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Attempt to coerce an expression to a type, and return the
/// adjusted type of the expression, if successful.
/// Adjustments are only recorded if the coercion succeeded.
/// The expressions *must not* have any pre-existing adjustments.
pub fn try_coerce(
&self,
2019-11-30 15:08:22 +01:00
expr: &hir::Expr<'_>,
expr_ty: Ty<'tcx>,
target: Ty<'tcx>,
allow_two_phase: AllowTwoPhase,
cause: Option<ObligationCause<'tcx>>,
) -> RelateResult<'tcx, Ty<'tcx>> {
let source = self.resolve_vars_with_obligations(expr_ty);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
let cause =
cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable));
let coerce = Coerce::new(self, cause, allow_two_phase);
let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;
let (adjustments, _) = self.register_infer_ok_obligations(ok);
self.apply_adjustments(expr, adjustments);
Ok(if expr_ty.references_error() { self.tcx.ty_error() } else { target })
}
/// Same as `try_coerce()`, but without side-effects.
///
/// Returns false if the coercion creates any obligations that result in
/// errors.
2017-04-21 13:28:31 +02:00
pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool {
let source = self.resolve_vars_with_obligations(expr_ty);
debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
// We don't ever need two-phase here since we throw out the result of the coercion
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
self.probe(|_| {
let Ok(ok) = coerce.coerce(source, target) else {
return false;
};
let mut fcx = traits::FulfillmentContext::new_in_snapshot();
fcx.register_predicate_obligations(self, ok.obligations);
fcx.select_where_possible(&self).is_empty()
})
}
2020-05-01 21:56:10 +08:00
/// Given a type and a target type, this function will calculate and return
/// how many dereference steps needed to achieve `expr_ty <: target`. If
/// it's not possible, return `None`.
pub fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option<usize> {
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
// We don't ever need two-phase here since we throw out the result of the coercion
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
coerce
.autoderef(rustc_span::DUMMY_SP, expr_ty)
.find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps))
}
/// Given a type, this function will calculate and return the type given
/// for `<Ty as Deref>::Target` only if `Ty` also implements `DerefMut`.
///
/// This function is for diagnostics only, since it does not register
/// trait or region sub-obligations. (presumably we could, but it's not
/// particularly important for diagnostics...)
pub fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
self.autoderef(rustc_span::DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| {
self.infcx
.type_implements_trait(
self.tcx.lang_items().deref_mut_trait()?,
expr_ty,
ty::List::empty(),
self.param_env,
)
.may_apply()
.then(|| deref_ty)
})
}
/// Given some expressions, their known unified type and another expression,
/// tries to unify the types, potentially inserting coercions on any of the
/// provided expressions and returns their LUB (aka "common supertype").
///
/// This is really an internal helper. From outside the coercion
/// module, you should instantiate a `CoerceMany` instance.
2019-12-24 17:38:22 -05:00
fn try_find_coercion_lub<E>(
&self,
cause: &ObligationCause<'tcx>,
exprs: &[E],
prev_ty: Ty<'tcx>,
2019-11-30 15:08:22 +01:00
new: &hir::Expr<'_>,
2019-12-24 17:38:22 -05:00
new_ty: Ty<'tcx>,
) -> RelateResult<'tcx, Ty<'tcx>>
where
E: AsCoercionSite,
{
let prev_ty = self.resolve_vars_with_obligations(prev_ty);
let new_ty = self.resolve_vars_with_obligations(new_ty);
debug!(
"coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)",
prev_ty,
new_ty,
exprs.len()
);
2021-08-31 23:44:15 +02:00
// The following check fixes #88097, where the compiler erroneously
// attempted to coerce a closure type to itself via a function pointer.
if prev_ty == new_ty {
return Ok(prev_ty);
}
// Special-case that coercion alone cannot handle:
// Function items or non-capturing closures of differing IDs or InternalSubsts.
let (a_sig, b_sig) = {
2022-05-22 12:48:19 -07:00
#[allow(rustc::usage_of_ty_tykind)]
let is_capturing_closure = |ty: &ty::TyKind<'tcx>| {
if let &ty::Closure(closure_def_id, _substs) = ty {
self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some()
} else {
false
}
};
if is_capturing_closure(prev_ty.kind()) || is_capturing_closure(new_ty.kind()) {
(None, None)
} else {
match (prev_ty.kind(), new_ty.kind()) {
(ty::FnDef(..), ty::FnDef(..)) => {
// Don't reify if the function types have a LUB, i.e., they
// are the same function and their parameters have a LUB.
match self
.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
{
// We have a LUB of prev_ty and new_ty, just return it.
Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)),
Err(_) => {
(Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx)))
}
}
}
(ty::Closure(_, substs), ty::FnDef(..)) => {
let b_sig = new_ty.fn_sig(self.tcx);
let a_sig = self
.tcx
.signature_unclosure(substs.as_closure().sig(), b_sig.unsafety());
(Some(a_sig), Some(b_sig))
}
(ty::FnDef(..), ty::Closure(_, substs)) => {
let a_sig = prev_ty.fn_sig(self.tcx);
let b_sig = self
.tcx
.signature_unclosure(substs.as_closure().sig(), a_sig.unsafety());
(Some(a_sig), Some(b_sig))
}
(ty::Closure(_, substs_a), ty::Closure(_, substs_b)) => (
Some(self.tcx.signature_unclosure(
substs_a.as_closure().sig(),
hir::Unsafety::Normal,
)),
Some(self.tcx.signature_unclosure(
substs_b.as_closure().sig(),
hir::Unsafety::Normal,
)),
),
_ => (None, None),
}
}
};
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
// Intrinsics are not coercible to function pointers.
if a_sig.abi() == Abi::RustIntrinsic
|| a_sig.abi() == Abi::PlatformIntrinsic
|| b_sig.abi() == Abi::RustIntrinsic
|| b_sig.abi() == Abi::PlatformIntrinsic
{
return Err(TypeError::IntrinsicCast);
}
// The signature must match.
2020-10-24 02:21:18 +02:00
let a_sig = self.normalize_associated_types_in(new.span, a_sig);
let b_sig = self.normalize_associated_types_in(new.span, b_sig);
2019-12-24 17:38:22 -05:00
let sig = self
.at(cause, self.param_env)
.trace(prev_ty, new_ty)
2020-06-24 23:14:18 +02:00
.lub(a_sig, b_sig)
2019-12-24 17:38:22 -05:00
.map(|ok| self.register_infer_ok_obligations(ok))?;
// Reify both sides and return the reified fn pointer type.
let fn_ptr = self.tcx.mk_fn_ptr(sig);
2020-08-03 00:49:11 +02:00
let prev_adjustment = match prev_ty.kind() {
ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())),
ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
_ => unreachable!(),
};
2020-08-03 00:49:11 +02:00
let next_adjustment = match new_ty.kind() {
ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())),
ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
_ => unreachable!(),
};
for expr in exprs.iter().map(|e| e.as_coercion_site()) {
2019-12-24 17:38:22 -05:00
self.apply_adjustments(
expr,
vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }],
2019-12-24 17:38:22 -05:00
);
}
self.apply_adjustments(new, vec![Adjustment { kind: next_adjustment, target: fn_ptr }]);
return Ok(fn_ptr);
}
// Configure a Coerce instance to compute the LUB.
// We don't allow two-phase borrows on any autorefs this creates since we
// probably aren't processing function arguments here and even if we were,
// they're going to get autorefed again anyway and we can apply 2-phase borrows
// at that time.
let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No);
coerce.use_lub = true;
// First try to coerce the new expression to the type of the previous ones,
// but only if the new expression has no coercion already applied to it.
let mut first_error = None;
2020-07-17 08:47:04 +00:00
if !self.typeck_results.borrow().adjustments().contains_key(new.hir_id) {
let result = self.commit_if_ok(|_| coerce.coerce(new_ty, prev_ty));
match result {
Ok(ok) => {
let (adjustments, target) = self.register_infer_ok_obligations(ok);
self.apply_adjustments(new, adjustments);
debug!(
"coercion::try_find_coercion_lub: was able to coerce from new type {:?} to previous type {:?} ({:?})",
new_ty, prev_ty, target
);
return Ok(target);
}
Err(e) => first_error = Some(e),
}
}
// Then try to coerce the previous expressions to the type of the new one.
// This requires ensuring there are no coercions applied to *any* of the
// previous expressions, other than noop reborrows (ignoring lifetimes).
for expr in exprs {
let expr = expr.as_coercion_site();
2020-07-17 08:47:04 +00:00
let noop = match self.typeck_results.borrow().expr_adjustments(expr) {
2019-12-24 17:38:22 -05:00
&[
Adjustment { kind: Adjust::Deref(_), .. },
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl_adj)), .. },
] => {
2020-08-03 00:49:11 +02:00
match *self.node_ty(expr.hir_id).kind() {
ty::Ref(_, _, mt_orig) => {
let mutbl_adj: hir::Mutability = mutbl_adj.into();
// Reborrow that we can safely ignore, because
// the next adjustment can only be a Deref
// which will be merged into it.
mutbl_adj == mt_orig
}
_ => false,
}
}
&[Adjustment { kind: Adjust::NeverToAny, .. }] | &[] => true,
_ => false,
};
if !noop {
debug!(
"coercion::try_find_coercion_lub: older expression {:?} had adjustments, requiring LUB",
expr,
);
2019-12-24 17:38:22 -05:00
return self
.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
.map(|ok| self.register_infer_ok_obligations(ok));
}
}
match self.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) {
Err(_) => {
// Avoid giving strange errors on failed attempts.
if let Some(e) = first_error {
Err(e)
} else {
2019-12-24 17:38:22 -05:00
self.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
.map(|ok| self.register_infer_ok_obligations(ok))
}
}
Ok(ok) => {
let (adjustments, target) = self.register_infer_ok_obligations(ok);
for expr in exprs {
let expr = expr.as_coercion_site();
self.apply_adjustments(expr, adjustments.clone());
}
debug!(
"coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})",
prev_ty, new_ty, target
);
Ok(target)
}
}
}
}
/// CoerceMany encapsulates the pattern you should use when you have
/// many expressions that are all getting coerced to a common
/// type. This arises, for example, when you have a match (the result
/// of each arm is coerced to a common type). It also arises in less
/// obvious places, such as when you have many `break foo` expressions
/// that target the same loop, or the various `return` expressions in
/// a function.
///
/// The basic protocol is as follows:
///
/// - Instantiate the `CoerceMany` with an initial `expected_ty`.
/// This will also serve as the "starting LUB". The expectation is
/// that this type is something which all of the expressions *must*
/// be coercible to. Use a fresh type variable if needed.
/// - For each expression whose result is to be coerced, invoke `coerce()` with.
/// - In some cases we wish to coerce "non-expressions" whose types are implicitly
/// unit. This happens for example if you have a `break` with no expression,
/// or an `if` with no `else`. In that case, invoke `coerce_forced_unit()`.
/// - `coerce()` and `coerce_forced_unit()` may report errors. They hide this
/// from you so that you don't have to worry your pretty head about it.
/// But if an error is reported, the final type will be `err`.
/// - Invoking `coerce()` may cause us to go and adjust the "adjustments" on
/// previously coerced expressions.
/// - When all done, invoke `complete()`. This will return the LUB of
/// all your expressions.
/// - WARNING: I don't believe this final type is guaranteed to be
/// related to your initial `expected_ty` in any particular way,
/// although it will typically be a subtype, so you should check it.
/// - Invoking `complete()` may cause us to go and adjust the "adjustments" on
/// previously coerced expressions.
///
/// Example:
///
2022-04-15 15:04:34 -07:00
/// ```ignore (illustrative)
/// let mut coerce = CoerceMany::new(expected_ty);
/// for expr in exprs {
/// let expr_ty = fcx.check_expr_with_expectation(expr, expected);
/// coerce.coerce(fcx, &cause, expr, expr_ty);
/// }
/// let final_ty = coerce.complete(fcx);
/// ```
2019-06-14 00:48:52 +03:00
pub struct CoerceMany<'tcx, 'exprs, E: AsCoercionSite> {
expected_ty: Ty<'tcx>,
final_ty: Option<Ty<'tcx>>,
2019-06-14 00:48:52 +03:00
expressions: Expressions<'tcx, 'exprs, E>,
pushed: usize,
}
/// The type of a `CoerceMany` that is storing up the expressions into
/// a buffer. We use this in `check/mod.rs` for things like `break`.
2019-11-30 15:08:22 +01:00
pub type DynamicCoerceMany<'tcx> = CoerceMany<'tcx, 'tcx, &'tcx hir::Expr<'tcx>>;
2019-06-14 00:48:52 +03:00
enum Expressions<'tcx, 'exprs, E: AsCoercionSite> {
2019-11-30 15:08:22 +01:00
Dynamic(Vec<&'tcx hir::Expr<'tcx>>),
UpFront(&'exprs [E]),
}
2019-06-14 00:48:52 +03:00
impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
/// The usual case; collect the set of expressions dynamically.
/// If the full set of coercion sites is known before hand,
/// consider `with_coercion_sites()` instead to avoid allocation.
pub fn new(expected_ty: Ty<'tcx>) -> Self {
Self::make(expected_ty, Expressions::Dynamic(vec![]))
}
/// As an optimization, you can create a `CoerceMany` with a
/// pre-existing slice of expressions. In this case, you are
/// expected to pass each element in the slice to `coerce(...)` in
/// order. This is used with arrays in particular to avoid
/// needlessly cloning the slice.
2019-12-24 17:38:22 -05:00
pub fn with_coercion_sites(expected_ty: Ty<'tcx>, coercion_sites: &'exprs [E]) -> Self {
Self::make(expected_ty, Expressions::UpFront(coercion_sites))
}
2019-06-14 00:48:52 +03:00
fn make(expected_ty: Ty<'tcx>, expressions: Expressions<'tcx, 'exprs, E>) -> Self {
2019-12-24 17:38:22 -05:00
CoerceMany { expected_ty, final_ty: None, expressions, pushed: 0 }
}
2019-02-08 14:53:55 +01:00
/// Returns the "expected type" with which this coercion was
/// constructed. This represents the "downward propagated" type
/// that was given to us at the start of typing whatever construct
/// we are typing (e.g., the match expression).
///
/// Typically, this is used as the expected type when
/// type-checking each of the alternative expressions whose types
/// we are trying to merge.
pub fn expected_ty(&self) -> Ty<'tcx> {
self.expected_ty
}
/// Returns the current "merged type", representing our best-guess
/// at the LUB of the expressions we've seen so far (if any). This
/// isn't *final* until you call `self.complete()`, which will return
/// the merged type.
pub fn merged_ty(&self) -> Ty<'tcx> {
self.final_ty.unwrap_or(self.expected_ty)
}
/// Indicates that the value generated by `expression`, which is
2018-08-19 03:40:50 +01:00
/// of type `expression_ty`, is one of the possibilities that we
/// could coerce from. This will record `expression`, and later
/// calls to `coerce` may come back and add adjustments and things
/// if necessary.
pub fn coerce<'a>(
&mut self,
fcx: &FnCtxt<'a, 'tcx>,
cause: &ObligationCause<'tcx>,
2019-11-30 15:08:22 +01:00
expression: &'tcx hir::Expr<'tcx>,
expression_ty: Ty<'tcx>,
) {
2019-12-24 17:38:22 -05:00
self.coerce_inner(fcx, cause, Some(expression), expression_ty, None, false)
}
/// Indicates that one of the inputs is a "forced unit". This
/// occurs in a case like `if foo { ... };`, where the missing else
/// generates a "forced unit". Another example is a `loop { break;
/// }`, where the `break` has no argument expression. We treat
/// these cases slightly differently for error-reporting
/// purposes. Note that these tend to correspond to cases where
/// the `()` expression is implicit in the source, and hence we do
/// not take an expression argument.
///
/// The `augment_error` gives you a chance to extend the error
/// message, in case any results (e.g., we use this to suggest
/// removing a `;`).
pub fn coerce_forced_unit<'a>(
&mut self,
fcx: &FnCtxt<'a, 'tcx>,
cause: &ObligationCause<'tcx>,
augment_error: &mut dyn FnMut(&mut Diagnostic),
label_unit_as_expected: bool,
) {
2019-12-24 17:38:22 -05:00
self.coerce_inner(
fcx,
cause,
None,
fcx.tcx.mk_unit(),
Some(augment_error),
label_unit_as_expected,
)
}
/// The inner coercion "engine". If `expression` is `None`, this
/// is a forced-unit case, and hence `expression_ty` must be
/// `Nil`.
2021-08-23 22:21:21 +00:00
#[instrument(skip(self, fcx, augment_error, label_expression_as_expected), level = "debug")]
pub(crate) fn coerce_inner<'a>(
&mut self,
fcx: &FnCtxt<'a, 'tcx>,
cause: &ObligationCause<'tcx>,
2019-11-30 15:08:22 +01:00
expression: Option<&'tcx hir::Expr<'tcx>>,
mut expression_ty: Ty<'tcx>,
augment_error: Option<&mut dyn FnMut(&mut Diagnostic)>,
label_expression_as_expected: bool,
) {
// Incorporate whatever type inference information we have
// until now; in principle we might also want to process
// pending obligations, but doing so should only improve
// compatibility (hopefully that is true) by helping us
// uncover never types better.
if expression_ty.is_ty_var() {
expression_ty = fcx.infcx.shallow_resolve(expression_ty);
}
// If we see any error types, just propagate that error
// upwards.
if expression_ty.references_error() || self.merged_ty().references_error() {
self.final_ty = Some(fcx.tcx.ty_error());
return;
}
// Handle the actual type unification etc.
let result = if let Some(expression) = expression {
if self.pushed == 0 {
// Special-case the first expression we are coercing.
// To be honest, I'm not entirely sure why we do this.
// We don't allow two-phase borrows, see comment in try_find_coercion_lub for why
fcx.try_coerce(
expression,
expression_ty,
self.expected_ty,
AllowTwoPhase::No,
Some(cause.clone()),
)
} else {
match self.expressions {
Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub(
cause,
exprs,
self.merged_ty(),
expression,
expression_ty,
),
Expressions::UpFront(ref coercion_sites) => fcx.try_find_coercion_lub(
cause,
&coercion_sites[0..self.pushed],
self.merged_ty(),
expression,
expression_ty,
),
}
}
} else {
// this is a hack for cases where we default to `()` because
// the expression etc has been omitted from the source. An
// example is an `if let` without an else:
//
// if let Some(x) = ... { }
//
// we wind up with a second match arm that is like `_ =>
// ()`. That is the case we are considering here. We take
// a different path to get the right "expected, found"
// message and so forth (and because we know that
// `expression_ty` will be unit).
//
// Another example is `break` with no argument expression.
2018-09-11 23:17:35 +09:00
assert!(expression_ty.is_unit(), "if let hack without unit type");
fcx.at(cause, fcx.param_env)
2019-12-24 17:38:22 -05:00
.eq_exp(label_expression_as_expected, expression_ty, self.merged_ty())
.map(|infer_ok| {
fcx.register_infer_ok_obligations(infer_ok);
expression_ty
})
};
debug!(?result);
match result {
Ok(v) => {
self.final_ty = Some(v);
if let Some(e) = expression {
match self.expressions {
Expressions::Dynamic(ref mut buffer) => buffer.push(e),
Expressions::UpFront(coercion_sites) => {
// if the user gave us an array to validate, check that we got
// the next expression in the list, as expected
2019-12-24 17:38:22 -05:00
assert_eq!(
coercion_sites[self.pushed].as_coercion_site().hir_id,
e.hir_id
);
}
}
self.pushed += 1;
}
}
2019-09-27 18:35:34 -07:00
Err(coercion_error) => {
// Mark that we've failed to coerce the types here to suppress
// any superfluous errors we might encounter while trying to
// emit or provide suggestions on how to fix the initial error.
fcx.set_tainted_by_errors();
let (expected, found) = if label_expression_as_expected {
2017-04-28 18:01:06 -04:00
// In the case where this is a "forced unit", like
// `break`, we want to call the `()` "expected"
// since it is implied by the syntax.
// (Note: not all force-units work this way.)"
(expression_ty, self.merged_ty())
} else {
// Otherwise, the "expected" type for error
// reporting is the current unification type,
// which is basically the LUB of the expressions
// we've seen so far (combined with the expected
// type)
(self.merged_ty(), expression_ty)
};
let (expected, found) = fcx.resolve_vars_if_possible((expected, found));
2019-09-27 18:35:34 -07:00
let mut err;
let mut unsized_return = false;
let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
match *cause.code() {
ObligationCauseCode::ReturnNoExpression => {
2019-09-27 18:35:34 -07:00
err = struct_span_err!(
2019-12-24 17:38:22 -05:00
fcx.tcx.sess,
cause.span,
E0069,
"`return;` in a function whose return type is not `()`"
);
2019-09-27 18:35:34 -07:00
err.span_label(cause.span, "return type is not `()`");
}
ObligationCauseCode::BlockTailExpression(blk_id) => {
let parent_id = fcx.tcx.hir().get_parent_node(blk_id);
2019-09-27 18:35:34 -07:00
err = self.report_return_mismatched_types(
2019-01-21 15:13:59 -08:00
cause,
expected,
found,
coercion_error.clone(),
2019-01-21 15:13:59 -08:00
fcx,
parent_id,
expression,
Some(blk_id),
);
if !fcx.tcx.features().unsized_locals {
2020-01-14 13:18:06 -08:00
unsized_return = self.is_return_ty_unsized(fcx, blk_id);
}
if let Some(expression) = expression
&& let hir::ExprKind::Loop(loop_blk, ..) = expression.kind {
intravisit::walk_block(& mut visitor, loop_blk);
}
}
ObligationCauseCode::ReturnValue(id) => {
2019-09-27 18:35:34 -07:00
err = self.report_return_mismatched_types(
2019-12-24 17:38:22 -05:00
cause,
expected,
found,
coercion_error.clone(),
2019-12-24 17:38:22 -05:00
fcx,
id,
expression,
2019-12-24 17:38:22 -05:00
None,
);
if !fcx.tcx.features().unsized_locals {
let id = fcx.tcx.hir().get_parent_node(id);
2020-01-14 13:18:06 -08:00
unsized_return = self.is_return_ty_unsized(fcx, id);
}
}
2019-01-18 00:12:09 -08:00
_ => {
err = fcx.err_ctxt().report_mismatched_types(
cause,
expected,
found,
coercion_error.clone(),
);
2019-01-18 00:12:09 -08:00
}
}
if let Some(augment_error) = augment_error {
2019-09-27 18:35:34 -07:00
augment_error(&mut err);
}
let is_insufficiently_polymorphic =
matches!(coercion_error, TypeError::RegionsInsufficientlyPolymorphic(..));
if !is_insufficiently_polymorphic && let Some(expr) = expression {
fcx.emit_coerce_suggestions(
&mut err,
expr,
found,
expected,
None,
Implementation for 65853 This attempts to bring better error messages to invalid method calls, by applying some heuristics to identify common mistakes. The algorithm is inspired by Levenshtein distance and longest common sub-sequence. In essence, we treat the types of the function, and the types of the arguments you provided as two "words" and compute the edits to get from one to the other. We then modify that algorithm to detect 4 cases: - A function input is missing - An extra argument was provided - The type of an argument is straight up invalid - Two arguments have been swapped - A subset of the arguments have been shuffled (We detect the last two as separate cases so that we can detect two swaps, instead of 4 parameters permuted.) It helps to understand this argument by paying special attention to terminology: "inputs" refers to the inputs being *expected* by the function, and "arguments" refers to what has been provided at the call site. The basic sketch of the algorithm is as follows: - Construct a boolean grid, with a row for each argument, and a column for each input. The cell [i, j] is true if the i'th argument could satisfy the j'th input. - If we find an argument that could satisfy no inputs, provided for an input that can't be satisfied by any other argument, we consider this an "invalid type". - Extra arguments are those that can't satisfy any input, provided for an input that *could* be satisfied by another argument. - Missing inputs are inputs that can't be satisfied by any argument, where the provided argument could satisfy another input - Swapped / Permuted arguments are identified with a cycle detection algorithm. As each issue is found, we remove the relevant inputs / arguments and check for more issues. If we find no issues, we match up any "valid" arguments, and start again. Note that there's a lot of extra complexity: - We try to stay efficient on the happy path, only computing the diagonal until we find a problem, and then filling in the rest of the matrix. - Closure arguments are wrapped in a tuple and need to be unwrapped - We need to resolve closure types after the rest, to allow the most specific type constraints - We need to handle imported C functions that might be variadic in their inputs. I tried to document a lot of this in comments in the code and keep the naming clear.
2022-01-21 23:50:54 -05:00
Some(coercion_error),
);
}
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
}
err.emit_unless(unsized_return);
self.final_ty = Some(fcx.tcx.ty_error());
}
}
}
fn note_unreachable_loop_return(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'tcx>,
ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
) {
let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;};
let mut span: MultiSpan = vec![loop_span].into();
2022-08-09 12:27:53 +09:00
span.push_span_label(loop_span, "this might have zero elements to iterate on");
const MAXITER: usize = 3;
let iter = ret_exprs.iter().take(MAXITER);
for ret_expr in iter {
span.push_span_label(
ret_expr.span,
2022-08-09 12:27:53 +09:00
"if the loop doesn't execute, this value would never get returned",
);
}
err.span_note(
span,
"the function expects a value to always be returned, but loops might run zero times",
);
if MAXITER < ret_exprs.len() {
err.note(&format!(
"if the loop doesn't execute, {} other values would never get returned",
ret_exprs.len() - MAXITER
));
}
err.help(
"return a value for the case when the loop has zero elements to iterate on, or \
consider changing the return type to account for that possibility",
);
}
2019-01-21 15:13:59 -08:00
fn report_return_mismatched_types<'a>(
&self,
cause: &ObligationCause<'tcx>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
2019-09-27 18:35:34 -07:00
ty_err: TypeError<'tcx>,
2019-06-14 00:48:52 +03:00
fcx: &FnCtxt<'a, 'tcx>,
2019-02-24 09:33:17 +01:00
id: hir::HirId,
expression: Option<&'tcx hir::Expr<'tcx>>,
blk_id: Option<hir::HirId>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err);
2019-01-21 15:13:59 -08:00
let mut pointing_at_return_type = false;
let mut fn_output = None;
2019-01-21 15:13:59 -08:00
let parent_id = fcx.tcx.hir().get_parent_node(id);
let parent = fcx.tcx.hir().get(parent_id);
if let Some(expr) = expression
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), .. }) = parent
&& !matches!(fcx.tcx.hir().body(body).value.kind, hir::ExprKind::Block(..))
{
fcx.suggest_missing_semicolon(&mut err, expr, expected, true);
}
2019-01-21 15:13:59 -08:00
// Verify that this is a tail expression of a function, otherwise the
// label pointing out the cause for the type coercion will be wrong
// as prior return coercions would not be relevant (#57664).
let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
pointing_at_return_type =
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
2021-01-01 15:38:11 -03:00
if let (Some(cond_expr), true, false) = (
fcx.tcx.hir().get_if_cause(expr.hir_id),
expected.is_unit(),
pointing_at_return_type,
)
// If the block is from an external macro or try (`?`) desugaring, then
// do not suggest adding a semicolon, because there's nowhere to put it.
// See issues #81943 and #87051.
&& matches!(
2021-08-19 16:57:15 -05:00
cond_expr.span.desugaring_kind(),
None | Some(DesugaringKind::WhileLoop)
) && !in_external_macro(fcx.tcx.sess, cond_expr.span)
&& !matches!(
cond_expr.kind,
hir::ExprKind::Match(.., hir::MatchSource::TryDesugar)
)
{
err.span_label(cond_expr.span, "expected this to be `()`");
if expr.can_have_side_effects() {
fcx.suggest_semicolon_at_end(cond_expr.span, &mut err);
}
}
2019-01-21 15:13:59 -08:00
fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
} else {
fcx.get_fn_decl(parent_id)
};
if let Some((fn_decl, can_suggest)) = fn_decl {
if blk_id.is_none() {
2019-01-21 15:13:59 -08:00
pointing_at_return_type |= fcx.suggest_missing_return_type(
2019-12-24 17:38:22 -05:00
&mut err,
&fn_decl,
expected,
found,
can_suggest,
fcx.tcx.hir().get_parent_item(id).into(),
2019-12-24 17:38:22 -05:00
);
2019-01-21 15:13:59 -08:00
}
if !pointing_at_return_type {
fn_output = Some(&fn_decl.output); // `impl Trait` return type
2019-01-21 15:13:59 -08:00
}
}
let parent_id = fcx.tcx.hir().get_parent_item(id);
let parent_item = fcx.tcx.hir().get_by_def_id(parent_id.def_id);
if let (Some(expr), Some(_), Some((fn_decl, _, _))) =
(expression, blk_id, fcx.get_node_fn_decl(parent_item))
{
2021-04-24 13:57:41 +02:00
fcx.suggest_missing_break_or_return_expr(
&mut err,
expr,
fn_decl,
expected,
found,
id,
parent_id.into(),
2021-04-24 13:57:41 +02:00
);
}
let ret_coercion_span = fcx.ret_coercion_span.get();
if let Some(sp) = ret_coercion_span
// If the closure has an explicit return type annotation, or if
// the closure's return type has been inferred from outside
// requirements (such as an Fn* trait bound), then a type error
// may occur at the first return expression we see in the closure
// (if it conflicts with the declared return type). Skip adding a
// note in this case, since it would be incorrect.
&& !fcx.return_type_pre_known
{
err.span_note(
sp,
&format!(
"return type inferred to be `{}` here",
expected
),
);
}
if let (Some(sp), Some(fn_output)) = (ret_coercion_span, fn_output) {
2021-01-23 11:47:38 +01:00
self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output);
2020-01-15 11:14:05 -08:00
}
2020-01-15 11:14:05 -08:00
err
}
fn add_impl_trait_explanation<'a>(
&self,
err: &mut Diagnostic,
2020-08-18 16:44:45 -07:00
cause: &ObligationCause<'tcx>,
2020-01-15 11:14:05 -08:00
fcx: &FnCtxt<'a, 'tcx>,
expected: Ty<'tcx>,
sp: Span,
2020-02-15 12:10:59 +09:00
fn_output: &hir::FnRetTy<'_>,
2020-01-15 11:14:05 -08:00
) {
let return_sp = fn_output.span();
2020-01-15 11:14:05 -08:00
err.span_label(return_sp, "expected because this return type...");
err.span_label(
sp,
format!("...is found to be `{}` here", fcx.resolve_vars_with_obligations(expected)),
);
let impl_trait_msg = "for information on `impl Trait`, see \
<https://doc.rust-lang.org/book/ch10-02-traits.html\
#returning-types-that-implement-traits>";
2020-01-15 11:14:05 -08:00
let trait_obj_msg = "for information on trait objects, see \
<https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
#using-trait-objects-that-allow-for-values-of-different-types>";
2020-01-15 11:14:05 -08:00
err.note("to return `impl Trait`, all returned values must be of the same type");
err.note(impl_trait_msg);
let snippet = fcx
.tcx
.sess
.source_map()
.span_to_snippet(return_sp)
.unwrap_or_else(|_| "dyn Trait".to_string());
let mut snippet_iter = snippet.split_whitespace();
let has_impl = snippet_iter.next().map_or(false, |s| s == "impl");
// Only suggest `Box<dyn Trait>` if `Trait` in `impl Trait` is object safe.
let mut is_object_safe = false;
if let hir::FnRetTy::Return(ty) = fn_output
// Get the return type.
&& let hir::TyKind::OpaqueDef(..) = ty.kind
{
let ty = <dyn AstConv<'_>>::ast_ty_to_ty(fcx, ty);
// Get the `impl Trait`'s `DefId`.
if let ty::Opaque(def_id, _) = ty.kind()
// Get the `impl Trait`'s `Item` so that we can get its trait bounds and
// get the `Trait`'s `DefId`.
&& let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) =
fcx.tcx.hir().expect_item(def_id.expect_local()).kind
{
// Are of this `impl Trait`'s traits object safe?
is_object_safe = bounds.iter().all(|bound| {
bound
.trait_ref()
.and_then(|t| t.trait_def_id())
.map_or(false, |def_id| {
fcx.tcx.object_safety_violations(def_id).is_empty()
})
})
}
};
2020-01-15 11:14:05 -08:00
if has_impl {
if is_object_safe {
err.multipart_suggestion(
"you could change the return type to be a boxed trait object",
vec![
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
(return_sp.shrink_to_hi(), ">".to_string()),
],
Applicability::MachineApplicable,
);
let sugg = [sp, cause.span]
2020-08-18 16:44:45 -07:00
.into_iter()
.flat_map(|sp| {
[
2020-08-18 16:44:45 -07:00
(sp.shrink_to_lo(), "Box::new(".to_string()),
(sp.shrink_to_hi(), ")".to_string()),
]
.into_iter()
})
.collect::<Vec<_>>();
err.multipart_suggestion(
"if you change the return type to expect trait objects, box the returned \
expressions",
sugg,
Applicability::MaybeIncorrect,
);
} else {
err.help(&format!(
"if the trait `{}` were object safe, you could return a boxed trait object",
&snippet[5..]
));
}
2020-01-15 11:14:05 -08:00
err.note(trait_obj_msg);
2019-01-21 15:13:59 -08:00
}
2020-08-18 16:44:45 -07:00
err.help("you could instead create a new `enum` with a variant for each returned type");
2019-01-21 15:13:59 -08:00
}
2021-12-13 21:45:08 -04:00
fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool {
2022-02-26 07:43:47 -03:00
if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id)
&& let hir::FnRetTy::Return(ty) = fn_decl.output
&& let ty = <dyn AstConv<'_>>::ast_ty_to_ty(fcx, ty)
&& let ty::Dynamic(..) = ty.kind()
{
return true;
2020-01-14 13:18:06 -08:00
}
false
}
2019-06-14 00:48:52 +03:00
pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> {
if let Some(final_ty) = self.final_ty {
final_ty
} else {
// If we only had inputs that were of type `!` (or no
// inputs at all), then the final type is `!`.
assert_eq!(self.pushed, 0);
fcx.tcx.types.never
}
}
}
/// Something that can be converted into an expression to which we can
/// apply a coercion.
pub trait AsCoercionSite {
2019-11-30 15:08:22 +01:00
fn as_coercion_site(&self) -> &hir::Expr<'_>;
}
2019-11-30 15:08:22 +01:00
impl AsCoercionSite for hir::Expr<'_> {
fn as_coercion_site(&self) -> &hir::Expr<'_> {
self
}
}
impl<'a, T> AsCoercionSite for &'a T
2019-12-24 17:38:22 -05:00
where
T: AsCoercionSite,
{
2019-11-30 15:08:22 +01:00
fn as_coercion_site(&self) -> &hir::Expr<'_> {
(**self).as_coercion_site()
}
}
impl AsCoercionSite for ! {
2019-11-30 15:08:22 +01:00
fn as_coercion_site(&self) -> &hir::Expr<'_> {
unreachable!()
}
}
2019-11-30 15:08:22 +01:00
impl AsCoercionSite for hir::Arm<'_> {
fn as_coercion_site(&self) -> &hir::Expr<'_> {
&self.body
}
}