mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 14:21:44 +00:00
Merge #9512
9512: Record coercion adjustments r=Veykril a=Veykril cc https://github.com/rust-analyzer/rust-analyzer/issues/9475 Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
eab5d985ec
9 changed files with 478 additions and 168 deletions
|
@ -109,7 +109,7 @@ impl<'a> PatCtxt<'a> {
|
||||||
self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
|
self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
|
||||||
unadjusted_pat,
|
unadjusted_pat,
|
||||||
|subpattern, ref_ty| Pat {
|
|subpattern, ref_ty| Pat {
|
||||||
ty: ref_ty.clone(),
|
ty: ref_ty.target.clone(),
|
||||||
kind: Box::new(PatKind::Deref { subpattern }),
|
kind: Box::new(PatKind::Deref { subpattern }),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability};
|
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::Body,
|
||||||
data::{ConstData, FunctionData, StaticData},
|
data::{ConstData, FunctionData, StaticData},
|
||||||
|
@ -34,10 +34,10 @@ use rustc_hash::FxHashMap;
|
||||||
use stdx::impl_from;
|
use stdx::impl_from;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy,
|
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
|
||||||
Goal, Interner, Substitution, TyBuilder, TyExt, TyKind,
|
to_assoc_type_id, AliasEq, AliasTy, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy,
|
||||||
|
Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lint has a false positive here. See the link below for details.
|
// This lint has a false positive here. See the link below for details.
|
||||||
|
@ -103,12 +103,20 @@ impl Default for BindingMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct InferOk {
|
pub(crate) struct InferOk<T> {
|
||||||
|
value: T,
|
||||||
goals: Vec<InEnvironment<Goal>>,
|
goals: Vec<InEnvironment<Goal>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> InferOk<T> {
|
||||||
|
fn map<U>(self, f: impl FnOnce(T) -> U) -> InferOk<U> {
|
||||||
|
InferOk { value: f(self.value), goals: self.goals }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct TypeError;
|
pub(crate) struct TypeError;
|
||||||
pub(crate) type InferResult = Result<InferOk, TypeError>;
|
pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum InferenceDiagnostic {
|
pub enum InferenceDiagnostic {
|
||||||
|
@ -133,6 +141,108 @@ impl Default for InternedStandardTypes {
|
||||||
InternedStandardTypes { unknown: TyKind::Error.intern(&Interner) }
|
InternedStandardTypes { unknown: TyKind::Error.intern(&Interner) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Represents coercing a value to a different type of value.
|
||||||
|
///
|
||||||
|
/// We transform values by following a number of `Adjust` steps in order.
|
||||||
|
/// See the documentation on variants of `Adjust` for more details.
|
||||||
|
///
|
||||||
|
/// Here are some common scenarios:
|
||||||
|
///
|
||||||
|
/// 1. The simplest cases are where a pointer is not adjusted fat vs thin.
|
||||||
|
/// Here the pointer will be dereferenced N times (where a dereference can
|
||||||
|
/// happen to raw or borrowed pointers or any smart pointer which implements
|
||||||
|
/// Deref, including Box<_>). The types of dereferences is given by
|
||||||
|
/// `autoderefs`. It can then be auto-referenced zero or one times, indicated
|
||||||
|
/// by `autoref`, to either a raw or borrowed pointer. In these cases unsize is
|
||||||
|
/// `false`.
|
||||||
|
///
|
||||||
|
/// 2. A thin-to-fat coercion involves unsizing the underlying data. We start
|
||||||
|
/// with a thin pointer, deref a number of times, unsize the underlying data,
|
||||||
|
/// then autoref. The 'unsize' phase may change a fixed length array to a
|
||||||
|
/// dynamically sized one, a concrete object to a trait object, or statically
|
||||||
|
/// sized struct to a dynamically sized one. E.g., &[i32; 4] -> &[i32] is
|
||||||
|
/// represented by:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// Deref(None) -> [i32; 4],
|
||||||
|
/// Borrow(AutoBorrow::Ref) -> &[i32; 4],
|
||||||
|
/// Unsize -> &[i32],
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note that for a struct, the 'deep' unsizing of the struct is not recorded.
|
||||||
|
/// E.g., `struct Foo<T> { x: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]>
|
||||||
|
/// The autoderef and -ref are the same as in the above example, but the type
|
||||||
|
/// stored in `unsize` is `Foo<[i32]>`, we don't store any further detail about
|
||||||
|
/// the underlying conversions from `[i32; 4]` to `[i32]`.
|
||||||
|
///
|
||||||
|
/// 3. Coercing a `Box<T>` to `Box<dyn Trait>` is an interesting special case. In
|
||||||
|
/// that case, we have the pointer we need coming in, so there are no
|
||||||
|
/// autoderefs, and no autoref. Instead we just do the `Unsize` transformation.
|
||||||
|
/// At some point, of course, `Box` should move out of the compiler, in which
|
||||||
|
/// case this is analogous to transforming a struct. E.g., Box<[i32; 4]> ->
|
||||||
|
/// Box<[i32]> is an `Adjust::Unsize` with the target `Box<[i32]>`.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Adjustment {
|
||||||
|
pub kind: Adjust,
|
||||||
|
pub target: Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Adjust {
|
||||||
|
/// Go from ! to any type.
|
||||||
|
NeverToAny,
|
||||||
|
/// Dereference once, producing a place.
|
||||||
|
Deref(Option<OverloadedDeref>),
|
||||||
|
/// Take the address and produce either a `&` or `*` pointer.
|
||||||
|
Borrow(AutoBorrow),
|
||||||
|
Pointer(PointerCast),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
|
||||||
|
/// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
|
||||||
|
/// The target type is `U` in both cases, with the region and mutability
|
||||||
|
/// being those shared by both the receiver and the returned reference.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct OverloadedDeref(Mutability);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum AutoBorrow {
|
||||||
|
/// Converts from T to &T.
|
||||||
|
Ref(Mutability),
|
||||||
|
/// Converts from T to *T.
|
||||||
|
RawPtr(Mutability),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PointerCast {
|
||||||
|
/// Go from a fn-item type to a fn-pointer type.
|
||||||
|
ReifyFnPointer,
|
||||||
|
|
||||||
|
/// Go from a safe fn pointer to an unsafe fn pointer.
|
||||||
|
UnsafeFnPointer,
|
||||||
|
|
||||||
|
/// Go from a non-capturing closure to an fn pointer or an unsafe fn pointer.
|
||||||
|
/// It cannot convert a closure that requires unsafe.
|
||||||
|
ClosureFnPointer(Safety),
|
||||||
|
|
||||||
|
/// Go from a mut raw pointer to a const raw pointer.
|
||||||
|
MutToConstPointer,
|
||||||
|
|
||||||
|
/// Go from `*const [T; N]` to `*const T`
|
||||||
|
ArrayToPointer,
|
||||||
|
|
||||||
|
/// Unsize a pointer/reference value, e.g., `&[T; n]` to
|
||||||
|
/// `&[T]`. Note that the source could be a thin or fat pointer.
|
||||||
|
/// This will do things like convert thin pointers to fat
|
||||||
|
/// pointers, or convert structs containing thin pointers to
|
||||||
|
/// structs containing fat pointers, or convert between fat
|
||||||
|
/// pointers. We don't store the details of how the transform is
|
||||||
|
/// done (in fact, we don't know that, because it might depend on
|
||||||
|
/// the precise type parameters). We just store the target
|
||||||
|
/// type. Codegen backends and miri figure out what has to be done
|
||||||
|
/// based on the precise source/target type at hand.
|
||||||
|
Unsize,
|
||||||
|
}
|
||||||
|
|
||||||
/// The result of type inference: A mapping from expressions and patterns to types.
|
/// The result of type inference: A mapping from expressions and patterns to types.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Default)]
|
#[derive(Clone, PartialEq, Eq, Debug, Default)]
|
||||||
|
@ -156,7 +266,8 @@ pub struct InferenceResult {
|
||||||
/// Interned Unknown to return references to.
|
/// Interned Unknown to return references to.
|
||||||
standard_types: InternedStandardTypes,
|
standard_types: InternedStandardTypes,
|
||||||
/// Stores the types which were implicitly dereferenced in pattern binding modes.
|
/// Stores the types which were implicitly dereferenced in pattern binding modes.
|
||||||
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
|
pub pat_adjustments: FxHashMap<PatId, Vec<Adjustment>>,
|
||||||
|
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InferenceResult {
|
impl InferenceResult {
|
||||||
|
@ -238,7 +349,7 @@ struct InferenceContext<'a> {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct BreakableContext {
|
struct BreakableContext {
|
||||||
may_break: bool,
|
may_break: bool,
|
||||||
break_ty: Ty,
|
coerce: CoerceMany,
|
||||||
label: Option<name::Name>,
|
label: Option<name::Name>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +414,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.result.type_of_expr.insert(expr, ty);
|
self.result.type_of_expr.insert(expr, ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_expr_adj(&mut self, expr: ExprId, adjustments: Vec<Adjustment>) {
|
||||||
|
self.result.expr_adjustments.insert(expr, adjustments);
|
||||||
|
}
|
||||||
|
|
||||||
fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) {
|
fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) {
|
||||||
self.result.method_resolutions.insert(expr, (func, subst));
|
self.result.method_resolutions.insert(expr, (func, subst));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Inference of closure parameter types based on the closure's expected type.
|
//! Inference of closure parameter types based on the closure's expected type.
|
||||||
|
|
||||||
use chalk_ir::{cast::Cast, AliasTy, FnSubst, WhereClause};
|
use chalk_ir::{cast::Cast, AliasTy, FnSubst, WhereClause};
|
||||||
use hir_def::HasModule;
|
use hir_def::{expr::ExprId, HasModule};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -14,6 +14,7 @@ use super::{Expectation, InferenceContext};
|
||||||
impl InferenceContext<'_> {
|
impl InferenceContext<'_> {
|
||||||
pub(super) fn deduce_closure_type_from_expectations(
|
pub(super) fn deduce_closure_type_from_expectations(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
closure_expr: ExprId,
|
||||||
closure_ty: &Ty,
|
closure_ty: &Ty,
|
||||||
sig_ty: &Ty,
|
sig_ty: &Ty,
|
||||||
expectation: &Expectation,
|
expectation: &Expectation,
|
||||||
|
@ -24,7 +25,7 @@ impl InferenceContext<'_> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
|
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
|
||||||
self.coerce(closure_ty, &expected_ty);
|
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty);
|
||||||
|
|
||||||
// Deduction based on the expected `dyn Fn` is done separately.
|
// Deduction based on the expected `dyn Fn` is done separately.
|
||||||
if let TyKind::Dyn(dyn_ty) = expected_ty.kind(&Interner) {
|
if let TyKind::Dyn(dyn_ty) = expected_ty.kind(&Interner) {
|
||||||
|
|
|
@ -5,32 +5,47 @@
|
||||||
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
|
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
|
||||||
//! `librustc_typeck/check/coercion.rs`.
|
//! `librustc_typeck/check/coercion.rs`.
|
||||||
|
|
||||||
use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
|
use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
|
||||||
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
|
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
autoderef, infer::TypeMismatch, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig,
|
autoderef,
|
||||||
Interner, Solution, Substitution, Ty, TyBuilder, TyExt, TyKind,
|
infer::{
|
||||||
|
Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, PointerCast,
|
||||||
|
TypeError, TypeMismatch,
|
||||||
|
},
|
||||||
|
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, InEnvironment, Interner, Solution,
|
||||||
|
Substitution, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{InEnvironment, InferOk, InferResult, InferenceContext, TypeError};
|
pub(crate) type CoerceResult = Result<InferOk<(Vec<Adjustment>, Ty)>, TypeError>;
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
/// Do not require any adjustments, i.e. coerce `x -> x`.
|
||||||
/// Unify two types, but may coerce the first one to the second one
|
fn identity(_: Ty) -> Vec<Adjustment> {
|
||||||
/// using "implicit coercion rules" if needed.
|
vec![]
|
||||||
pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
|
}
|
||||||
let from_ty = self.resolve_ty_shallow(from_ty);
|
|
||||||
let to_ty = self.resolve_ty_shallow(to_ty);
|
fn simple(kind: Adjust) -> impl FnOnce(Ty) -> Vec<Adjustment> {
|
||||||
match self.coerce_inner(from_ty, &to_ty) {
|
move |target| vec![Adjustment { kind, target }]
|
||||||
Ok(result) => {
|
}
|
||||||
self.table.register_infer_ok(result);
|
|
||||||
true
|
/// This always returns `Ok(...)`.
|
||||||
}
|
fn success(
|
||||||
Err(_) => {
|
adj: Vec<Adjustment>,
|
||||||
// FIXME deal with error
|
target: Ty,
|
||||||
false
|
goals: Vec<InEnvironment<Goal<Interner>>>,
|
||||||
}
|
) -> CoerceResult {
|
||||||
}
|
Ok(InferOk { goals, value: (adj, target) })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(super) struct CoerceMany {
|
||||||
|
expected_ty: Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoerceMany {
|
||||||
|
pub(super) fn new(expected: Ty) -> Self {
|
||||||
|
CoerceMany { expected_ty: expected }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge two types from different branches, with possible coercion.
|
/// Merge two types from different branches, with possible coercion.
|
||||||
|
@ -40,54 +55,92 @@ impl<'a> InferenceContext<'a> {
|
||||||
/// coerce both to function pointers;
|
/// coerce both to function pointers;
|
||||||
/// - if we were concerned with lifetime subtyping, we'd need to look for a
|
/// - if we were concerned with lifetime subtyping, we'd need to look for a
|
||||||
/// least upper bound.
|
/// least upper bound.
|
||||||
pub(super) fn coerce_merge_branch(&mut self, id: Option<ExprId>, ty1: &Ty, ty2: &Ty) -> Ty {
|
pub(super) fn coerce(
|
||||||
let ty1 = self.resolve_ty_shallow(ty1);
|
&mut self,
|
||||||
let ty2 = self.resolve_ty_shallow(ty2);
|
ctx: &mut InferenceContext<'_>,
|
||||||
|
expr: Option<ExprId>,
|
||||||
|
expr_ty: &Ty,
|
||||||
|
) {
|
||||||
|
let expr_ty = ctx.resolve_ty_shallow(expr_ty);
|
||||||
|
self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty);
|
||||||
|
|
||||||
// Special case: two function types. Try to coerce both to
|
// Special case: two function types. Try to coerce both to
|
||||||
// pointers to have a chance at getting a match. See
|
// pointers to have a chance at getting a match. See
|
||||||
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
|
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
|
||||||
let sig = match (ty1.kind(&Interner), ty2.kind(&Interner)) {
|
let sig = match (self.expected_ty.kind(&Interner), expr_ty.kind(&Interner)) {
|
||||||
(TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
|
(TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
|
||||||
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
|
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
|
||||||
// we should be coercing the closure to a fn pointer of the safety of the FnDef
|
// we should be coercing the closure to a fn pointer of the safety of the FnDef
|
||||||
cov_mark::hit!(coerce_fn_reification);
|
cov_mark::hit!(coerce_fn_reification);
|
||||||
let sig = ty1.callable_sig(self.db).expect("FnDef without callable sig");
|
let sig =
|
||||||
|
self.expected_ty.callable_sig(ctx.db).expect("FnDef without callable sig");
|
||||||
Some(sig)
|
Some(sig)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(sig) = sig {
|
if let Some(sig) = sig {
|
||||||
let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner);
|
let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner);
|
||||||
let result1 = self.coerce_inner(ty1.clone(), &target_ty);
|
let result1 = ctx.coerce_inner(self.expected_ty.clone(), &target_ty);
|
||||||
let result2 = self.coerce_inner(ty2.clone(), &target_ty);
|
let result2 = ctx.coerce_inner(expr_ty.clone(), &target_ty);
|
||||||
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
||||||
self.table.register_infer_ok(result1);
|
ctx.table.register_infer_ok(result1);
|
||||||
self.table.register_infer_ok(result2);
|
ctx.table.register_infer_ok(result2);
|
||||||
return target_ty;
|
return self.expected_ty = target_ty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// It might not seem like it, but order is important here: ty1 is our
|
// It might not seem like it, but order is important here: If the expected
|
||||||
// "previous" type, ty2 is the "new" one being added. If the previous
|
|
||||||
// type is a type variable and the new one is `!`, trying it the other
|
// type is a type variable and the new one is `!`, trying it the other
|
||||||
// way around first would mean we make the type variable `!`, instead of
|
// way around first would mean we make the type variable `!`, instead of
|
||||||
// just marking it as possibly diverging.
|
// just marking it as possibly diverging.
|
||||||
if self.coerce(&ty2, &ty1) {
|
if ctx.coerce(expr, &expr_ty, &self.expected_ty).is_ok() {
|
||||||
ty1
|
/* self.expected_ty is already correct */
|
||||||
} else if self.coerce(&ty1, &ty2) {
|
} else if ctx.coerce(expr, &self.expected_ty, &expr_ty).is_ok() {
|
||||||
ty2
|
self.expected_ty = expr_ty;
|
||||||
} else {
|
} else {
|
||||||
if let Some(id) = id {
|
if let Some(id) = expr {
|
||||||
self.result
|
ctx.result.type_mismatches.insert(
|
||||||
.type_mismatches
|
id.into(),
|
||||||
.insert(id.into(), TypeMismatch { expected: ty1.clone(), actual: ty2 });
|
TypeMismatch { expected: self.expected_ty.clone(), actual: expr_ty },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
cov_mark::hit!(coerce_merge_fail_fallback);
|
cov_mark::hit!(coerce_merge_fail_fallback);
|
||||||
ty1
|
/* self.expected_ty is already correct */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult {
|
pub(super) fn complete(self) -> Ty {
|
||||||
|
self.expected_ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> InferenceContext<'a> {
|
||||||
|
/// Unify two types, but may coerce the first one to the second one
|
||||||
|
/// using "implicit coercion rules" if needed.
|
||||||
|
pub(super) fn coerce(
|
||||||
|
&mut self,
|
||||||
|
expr: Option<ExprId>,
|
||||||
|
from_ty: &Ty,
|
||||||
|
to_ty: &Ty,
|
||||||
|
) -> InferResult<Ty> {
|
||||||
|
let from_ty = self.resolve_ty_shallow(from_ty);
|
||||||
|
let to_ty = self.resolve_ty_shallow(to_ty);
|
||||||
|
match self.coerce_inner(from_ty, &to_ty) {
|
||||||
|
Ok(InferOk { value: (adjustments, ty), goals }) => {
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
self.write_expr_adj(expr, adjustments);
|
||||||
|
}
|
||||||
|
self.table.register_infer_ok(InferOk { value: (), goals });
|
||||||
|
Ok(InferOk { value: ty, goals: Vec::new() })
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// FIXME deal with error
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult {
|
||||||
if from_ty.is_never() {
|
if from_ty.is_never() {
|
||||||
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
|
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
|
||||||
// type variable, we want `?T` to fallback to `!` if not
|
// type variable, we want `?T` to fallback to `!` if not
|
||||||
|
@ -96,13 +149,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
// let _: Option<?T> = Some({ return; });
|
// let _: Option<?T> = Some({ return; });
|
||||||
//
|
//
|
||||||
// here, we would coerce from `!` to `?T`.
|
// here, we would coerce from `!` to `?T`.
|
||||||
match to_ty.kind(&Interner) {
|
if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(&Interner) {
|
||||||
TyKind::InferenceVar(tv, TyVariableKind::General) => {
|
self.table.set_diverging(*tv, true);
|
||||||
self.table.set_diverging(*tv, true);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
return Ok(InferOk { goals: Vec::new() });
|
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consider coercing the subtype to a DST
|
// Consider coercing the subtype to a DST
|
||||||
|
@ -112,12 +162,8 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
// Examine the supertype and consider auto-borrowing.
|
// Examine the supertype and consider auto-borrowing.
|
||||||
match to_ty.kind(&Interner) {
|
match to_ty.kind(&Interner) {
|
||||||
TyKind::Raw(mt, _) => {
|
TyKind::Raw(mt, _) => return self.coerce_ptr(from_ty, to_ty, *mt),
|
||||||
return self.coerce_ptr(from_ty, to_ty, *mt);
|
TyKind::Ref(mt, _, _) => return self.coerce_ref(from_ty, to_ty, *mt),
|
||||||
}
|
|
||||||
TyKind::Ref(mt, _, _) => {
|
|
||||||
return self.coerce_ref(from_ty, to_ty, *mt);
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,35 +189,63 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Otherwise, just use unification rules.
|
// Otherwise, just use unification rules.
|
||||||
self.table.try_unify(&from_ty, to_ty)
|
self.unify_and(&from_ty, to_ty, identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult {
|
/// Unify two types (using sub or lub) and produce a specific coercion.
|
||||||
let (_is_ref, from_mt, from_inner) = match from_ty.kind(&Interner) {
|
fn unify_and<F>(&mut self, t1: &Ty, t2: &Ty, f: F) -> CoerceResult
|
||||||
|
where
|
||||||
|
F: FnOnce(Ty) -> Vec<Adjustment>,
|
||||||
|
{
|
||||||
|
self.table
|
||||||
|
.try_unify(t1, t2)
|
||||||
|
.and_then(|InferOk { goals, .. }| success(f(t1.clone()), t1.clone(), goals))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult {
|
||||||
|
let (is_ref, from_mt, from_inner) = match from_ty.kind(&Interner) {
|
||||||
TyKind::Ref(mt, _, ty) => (true, mt, ty),
|
TyKind::Ref(mt, _, ty) => (true, mt, ty),
|
||||||
TyKind::Raw(mt, ty) => (false, mt, ty),
|
TyKind::Raw(mt, ty) => (false, mt, ty),
|
||||||
_ => return self.table.try_unify(&from_ty, to_ty),
|
_ => return self.unify_and(&from_ty, to_ty, identity),
|
||||||
};
|
};
|
||||||
|
|
||||||
coerce_mutabilities(*from_mt, to_mt)?;
|
coerce_mutabilities(*from_mt, to_mt)?;
|
||||||
|
|
||||||
// Check that the types which they point at are compatible.
|
// Check that the types which they point at are compatible.
|
||||||
let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner);
|
let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner);
|
||||||
// FIXME: behavior differs based on is_ref once we're computing adjustments
|
|
||||||
self.table.try_unify(&from_raw, to_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(&from_raw, to_ty, |target| {
|
||||||
|
vec![
|
||||||
|
Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() },
|
||||||
|
Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(to_mt)), target },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
} else if *from_mt != to_mt {
|
||||||
|
self.unify_and(
|
||||||
|
&from_raw,
|
||||||
|
to_ty,
|
||||||
|
simple(Adjust::Pointer(PointerCast::MutToConstPointer)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.unify_and(&from_raw, to_ty, identity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
|
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
|
||||||
/// To match `A` with `B`, autoderef will be performed,
|
/// To match `A` with `B`, autoderef will be performed,
|
||||||
/// calling `deref`/`deref_mut` where necessary.
|
/// calling `deref`/`deref_mut` where necessary.
|
||||||
fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult {
|
fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult {
|
||||||
match from_ty.kind(&Interner) {
|
match from_ty.kind(&Interner) {
|
||||||
TyKind::Ref(mt, _, _) => {
|
TyKind::Ref(mt, _, _) => {
|
||||||
coerce_mutabilities(*mt, to_mt)?;
|
coerce_mutabilities(*mt, to_mt)?;
|
||||||
}
|
}
|
||||||
_ => return self.table.try_unify(&from_ty, to_ty),
|
_ => return self.unify_and(&from_ty, to_ty, identity),
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: this code is mostly copied and adapted from rustc, and
|
// NOTE: this code is mostly copied and adapted from rustc, and
|
||||||
|
@ -227,7 +301,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(&Interner);
|
let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(&Interner);
|
||||||
match self.table.try_unify(&derefd_from_ty, to_ty) {
|
match self.table.try_unify(&derefd_from_ty, to_ty) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
found = Some(result);
|
found = Some(result.map(|()| derefd_from_ty));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -243,19 +317,23 @@ impl<'a> InferenceContext<'a> {
|
||||||
// (e.g., in example above, the failure from relating `Vec<T>`
|
// (e.g., in example above, the failure from relating `Vec<T>`
|
||||||
// to the target type), since that should be the least
|
// to the target type), since that should be the least
|
||||||
// confusing.
|
// confusing.
|
||||||
let result = match found {
|
let InferOk { value: ty, goals } = match found {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => {
|
None => {
|
||||||
let err = first_error.expect("coerce_borrowed_pointer had no error");
|
let err = first_error.expect("coerce_borrowed_pointer had no error");
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// FIXME: record overloaded deref adjustments
|
||||||
Ok(result)
|
success(
|
||||||
|
vec![Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() }],
|
||||||
|
ty,
|
||||||
|
goals,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to coerce from the type of a Rust function item into a function pointer.
|
/// Attempts to coerce from the type of a Rust function item into a function pointer.
|
||||||
fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult {
|
fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult {
|
||||||
match to_ty.kind(&Interner) {
|
match to_ty.kind(&Interner) {
|
||||||
TyKind::Function(_) => {
|
TyKind::Function(_) => {
|
||||||
let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig");
|
let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig");
|
||||||
|
@ -267,11 +345,28 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
let from_sig = from_sig.to_fn_ptr();
|
let from_sig = from_sig.to_fn_ptr();
|
||||||
let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(&Interner);
|
let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(&Interner);
|
||||||
let ok = self.coerce_from_safe_fn(from_fn_pointer, &from_sig, to_ty)?;
|
let ok = self.coerce_from_safe_fn(
|
||||||
|
from_fn_pointer.clone(),
|
||||||
|
&from_sig,
|
||||||
|
to_ty,
|
||||||
|
|unsafe_ty| {
|
||||||
|
vec![
|
||||||
|
Adjustment {
|
||||||
|
kind: Adjust::Pointer(PointerCast::ReifyFnPointer),
|
||||||
|
target: from_fn_pointer,
|
||||||
|
},
|
||||||
|
Adjustment {
|
||||||
|
kind: Adjust::Pointer(PointerCast::UnsafeFnPointer),
|
||||||
|
target: unsafe_ty,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
simple(Adjust::Pointer(PointerCast::ReifyFnPointer)),
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(ok)
|
Ok(ok)
|
||||||
}
|
}
|
||||||
_ => self.table.try_unify(&from_ty, to_ty),
|
_ => self.unify_and(&from_ty, to_ty, identity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,26 +375,38 @@ impl<'a> InferenceContext<'a> {
|
||||||
from_ty: Ty,
|
from_ty: Ty,
|
||||||
from_f: &FnPointer,
|
from_f: &FnPointer,
|
||||||
to_ty: &Ty,
|
to_ty: &Ty,
|
||||||
) -> InferResult {
|
) -> CoerceResult {
|
||||||
self.coerce_from_safe_fn(from_ty, from_f, to_ty)
|
self.coerce_from_safe_fn(
|
||||||
|
from_ty,
|
||||||
|
from_f,
|
||||||
|
to_ty,
|
||||||
|
simple(Adjust::Pointer(PointerCast::UnsafeFnPointer)),
|
||||||
|
identity,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_from_safe_fn(
|
fn coerce_from_safe_fn<F, G>(
|
||||||
&mut self,
|
&mut self,
|
||||||
from_ty: Ty,
|
from_ty: Ty,
|
||||||
from_fn_ptr: &FnPointer,
|
from_fn_ptr: &FnPointer,
|
||||||
to_ty: &Ty,
|
to_ty: &Ty,
|
||||||
) -> InferResult {
|
to_unsafe: F,
|
||||||
|
normal: G,
|
||||||
|
) -> CoerceResult
|
||||||
|
where
|
||||||
|
F: FnOnce(Ty) -> Vec<Adjustment>,
|
||||||
|
G: FnOnce(Ty) -> Vec<Adjustment>,
|
||||||
|
{
|
||||||
if let TyKind::Function(to_fn_ptr) = to_ty.kind(&Interner) {
|
if let TyKind::Function(to_fn_ptr) = to_ty.kind(&Interner) {
|
||||||
if let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) =
|
if let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) =
|
||||||
(from_fn_ptr.sig.safety, to_fn_ptr.sig.safety)
|
(from_fn_ptr.sig.safety, to_fn_ptr.sig.safety)
|
||||||
{
|
{
|
||||||
let from_unsafe =
|
let from_unsafe =
|
||||||
TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(&Interner);
|
TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(&Interner);
|
||||||
return self.table.try_unify(&from_unsafe, to_ty);
|
return self.unify_and(&from_unsafe, to_ty, to_unsafe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.table.try_unify(&from_ty, to_ty)
|
self.unify_and(&from_ty, to_ty, normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to coerce from the type of a non-capturing closure into a
|
/// Attempts to coerce from the type of a non-capturing closure into a
|
||||||
|
@ -309,9 +416,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
from_ty: Ty,
|
from_ty: Ty,
|
||||||
from_substs: &Substitution,
|
from_substs: &Substitution,
|
||||||
to_ty: &Ty,
|
to_ty: &Ty,
|
||||||
) -> InferResult {
|
) -> CoerceResult {
|
||||||
match to_ty.kind(&Interner) {
|
match to_ty.kind(&Interner) {
|
||||||
TyKind::Function(fn_ty) /* if from_substs is non-capturing (FIXME) */ => {
|
// if from_substs is non-capturing (FIXME)
|
||||||
|
TyKind::Function(fn_ty) => {
|
||||||
// We coerce the closure, which has fn type
|
// We coerce the closure, which has fn type
|
||||||
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
|
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
|
||||||
// to
|
// to
|
||||||
|
@ -320,16 +428,20 @@ impl<'a> InferenceContext<'a> {
|
||||||
// `unsafe fn(arg0,arg1,...) -> _`
|
// `unsafe fn(arg0,arg1,...) -> _`
|
||||||
let safety = fn_ty.sig.safety;
|
let safety = fn_ty.sig.safety;
|
||||||
let pointer_ty = coerce_closure_fn_ty(from_substs, safety);
|
let pointer_ty = coerce_closure_fn_ty(from_substs, safety);
|
||||||
self.table.try_unify(&pointer_ty, to_ty)
|
self.unify_and(
|
||||||
|
&pointer_ty,
|
||||||
|
to_ty,
|
||||||
|
simple(Adjust::Pointer(PointerCast::ClosureFnPointer(safety))),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
_ => self.table.try_unify(&from_ty, to_ty),
|
_ => self.unify_and(&from_ty, to_ty, identity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
|
/// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
|
||||||
///
|
///
|
||||||
/// See: <https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html>
|
/// See: <https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html>
|
||||||
fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult {
|
fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult {
|
||||||
// These 'if' statements require some explanation.
|
// These 'if' statements require some explanation.
|
||||||
// The `CoerceUnsized` trait is special - it is only
|
// The `CoerceUnsized` trait is special - it is only
|
||||||
// possible to write `impl CoerceUnsized<B> for A` where
|
// possible to write `impl CoerceUnsized<B> for A` where
|
||||||
|
@ -341,7 +453,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
//
|
//
|
||||||
// Both of these trigger a special `CoerceUnsized`-related error (E0376)
|
// Both of these trigger a special `CoerceUnsized`-related error (E0376)
|
||||||
//
|
//
|
||||||
// We can take advantage of this fact to avoid performing unecessary work.
|
// 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
|
// 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`)
|
// 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
|
// or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for
|
||||||
|
@ -359,20 +471,34 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle reborrows before trying to solve `Source: CoerceUnsized<Target>`.
|
// Handle reborrows before trying to solve `Source: CoerceUnsized<Target>`.
|
||||||
let coerce_from = match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
|
let reborrow = match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
|
||||||
(TyKind::Ref(from_mt, _, from_inner), TyKind::Ref(to_mt, _, _)) => {
|
(TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => {
|
||||||
coerce_mutabilities(*from_mt, *to_mt)?;
|
coerce_mutabilities(*from_mt, to_mt)?;
|
||||||
|
|
||||||
let lt = static_lifetime();
|
let lt = static_lifetime();
|
||||||
TyKind::Ref(*to_mt, lt, from_inner.clone()).intern(&Interner)
|
Some((
|
||||||
|
Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() },
|
||||||
|
Adjustment {
|
||||||
|
kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)),
|
||||||
|
target: TyKind::Ref(to_mt, lt, from_inner.clone()).intern(&Interner),
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
(TyKind::Ref(from_mt, _, from_inner), TyKind::Raw(to_mt, _)) => {
|
(TyKind::Ref(from_mt, _, from_inner), &TyKind::Raw(to_mt, _)) => {
|
||||||
coerce_mutabilities(*from_mt, *to_mt)?;
|
coerce_mutabilities(*from_mt, to_mt)?;
|
||||||
|
|
||||||
TyKind::Raw(*to_mt, from_inner.clone()).intern(&Interner)
|
Some((
|
||||||
|
Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() },
|
||||||
|
Adjustment {
|
||||||
|
kind: Adjust::Borrow(AutoBorrow::RawPtr(to_mt)),
|
||||||
|
target: TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner),
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
_ => from_ty.clone(),
|
_ => None,
|
||||||
};
|
};
|
||||||
|
let coerce_from =
|
||||||
|
reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone());
|
||||||
|
|
||||||
let krate = self.resolver.krate().unwrap();
|
let krate = self.resolver.krate().unwrap();
|
||||||
let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) {
|
let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) {
|
||||||
|
@ -417,8 +543,13 @@ impl<'a> InferenceContext<'a> {
|
||||||
// FIXME: should we accept ambiguous results here?
|
// FIXME: should we accept ambiguous results here?
|
||||||
_ => return Err(TypeError),
|
_ => return Err(TypeError),
|
||||||
};
|
};
|
||||||
|
let unsize =
|
||||||
Ok(InferOk { goals: Vec::new() })
|
Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() };
|
||||||
|
let adjustments = match reborrow {
|
||||||
|
None => vec![unsize],
|
||||||
|
Some((deref, autoref)) => vec![deref, autoref, unsize],
|
||||||
|
};
|
||||||
|
success(adjustments, to_ty.clone(), vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ use syntax::ast::RangeOp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
autoderef, consteval,
|
autoderef, consteval,
|
||||||
|
infer::coerce::CoerceMany,
|
||||||
lower::lower_to_chalk_mutability,
|
lower::lower_to_chalk_mutability,
|
||||||
mapping::from_chalk,
|
mapping::from_chalk,
|
||||||
method_resolution, op,
|
method_resolution, op,
|
||||||
|
@ -56,15 +57,16 @@ impl<'a> InferenceContext<'a> {
|
||||||
pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
||||||
let ty = self.infer_expr_inner(expr, expected);
|
let ty = self.infer_expr_inner(expr, expected);
|
||||||
let ty = if let Some(target) = expected.only_has_type(&mut self.table) {
|
let ty = if let Some(target) = expected.only_has_type(&mut self.table) {
|
||||||
if !self.coerce(&ty, &target) {
|
match self.coerce(Some(expr), &ty, &target) {
|
||||||
self.result
|
Ok(res) => res.value,
|
||||||
.type_mismatches
|
Err(_) => {
|
||||||
.insert(expr.into(), TypeMismatch { expected: target, actual: ty.clone() });
|
self.result
|
||||||
// Return actual type when type mismatch.
|
.type_mismatches
|
||||||
// This is needed for diagnostic when return type mismatch.
|
.insert(expr.into(), TypeMismatch { expected: target, actual: ty.clone() });
|
||||||
ty
|
// Return actual type when type mismatch.
|
||||||
} else {
|
// This is needed for diagnostic when return type mismatch.
|
||||||
target
|
ty
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ty
|
ty
|
||||||
|
@ -124,31 +126,32 @@ impl<'a> InferenceContext<'a> {
|
||||||
let body = Arc::clone(&self.body); // avoid borrow checker problem
|
let body = Arc::clone(&self.body); // avoid borrow checker problem
|
||||||
let ty = match &body[tgt_expr] {
|
let ty = match &body[tgt_expr] {
|
||||||
Expr::Missing => self.err_ty(),
|
Expr::Missing => self.err_ty(),
|
||||||
Expr::If { condition, then_branch, else_branch } => {
|
&Expr::If { condition, then_branch, else_branch } => {
|
||||||
// if let is desugared to match, so this is always simple if
|
// if let is desugared to match, so this is always simple if
|
||||||
self.infer_expr(
|
self.infer_expr(
|
||||||
*condition,
|
condition,
|
||||||
&Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(&Interner)),
|
&Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(&Interner)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
let mut both_arms_diverge = Diverges::Always;
|
let mut both_arms_diverge = Diverges::Always;
|
||||||
|
|
||||||
let mut result_ty = self.table.new_type_var();
|
let result_ty = self.table.new_type_var();
|
||||||
let then_ty = self.infer_expr_inner(*then_branch, expected);
|
let then_ty = self.infer_expr_inner(then_branch, expected);
|
||||||
both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
|
both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty);
|
let mut coerce = CoerceMany::new(result_ty);
|
||||||
|
coerce.coerce(self, Some(then_branch), &then_ty);
|
||||||
let else_ty = match else_branch {
|
let else_ty = match else_branch {
|
||||||
Some(else_branch) => self.infer_expr_inner(*else_branch, expected),
|
Some(else_branch) => self.infer_expr_inner(else_branch, expected),
|
||||||
None => TyBuilder::unit(),
|
None => TyBuilder::unit(),
|
||||||
};
|
};
|
||||||
both_arms_diverge &= self.diverges;
|
both_arms_diverge &= self.diverges;
|
||||||
// FIXME: create a synthetic `else {}` so we have something to refer to here instead of None?
|
// FIXME: create a synthetic `else {}` so we have something to refer to here instead of None?
|
||||||
result_ty = self.coerce_merge_branch(*else_branch, &result_ty, &else_ty);
|
coerce.coerce(self, else_branch, &else_ty);
|
||||||
|
|
||||||
self.diverges = condition_diverges | both_arms_diverge;
|
self.diverges = condition_diverges | both_arms_diverge;
|
||||||
|
|
||||||
result_ty
|
coerce.complete()
|
||||||
}
|
}
|
||||||
Expr::Block { statements, tail, label, id: _ } => {
|
Expr::Block { statements, tail, label, id: _ } => {
|
||||||
let old_resolver = mem::replace(
|
let old_resolver = mem::replace(
|
||||||
|
@ -160,19 +163,23 @@ impl<'a> InferenceContext<'a> {
|
||||||
let break_ty = self.table.new_type_var();
|
let break_ty = self.table.new_type_var();
|
||||||
self.breakables.push(BreakableContext {
|
self.breakables.push(BreakableContext {
|
||||||
may_break: false,
|
may_break: false,
|
||||||
break_ty: break_ty.clone(),
|
coerce: CoerceMany::new(break_ty.clone()),
|
||||||
label: label.map(|label| self.body[label].name.clone()),
|
label: label.map(|label| self.body[label].name.clone()),
|
||||||
});
|
});
|
||||||
let ty =
|
let ty = self.infer_block(
|
||||||
self.infer_block(statements, *tail, &Expectation::has_type(break_ty));
|
tgt_expr,
|
||||||
|
statements,
|
||||||
|
*tail,
|
||||||
|
&Expectation::has_type(break_ty),
|
||||||
|
);
|
||||||
let ctxt = self.breakables.pop().expect("breakable stack broken");
|
let ctxt = self.breakables.pop().expect("breakable stack broken");
|
||||||
if ctxt.may_break {
|
if ctxt.may_break {
|
||||||
ctxt.break_ty
|
ctxt.coerce.complete()
|
||||||
} else {
|
} else {
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => self.infer_block(statements, *tail, expected),
|
None => self.infer_block(tgt_expr, statements, *tail, expected),
|
||||||
};
|
};
|
||||||
self.resolver = old_resolver;
|
self.resolver = old_resolver;
|
||||||
ty
|
ty
|
||||||
|
@ -185,7 +192,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
Expr::Async { body } => {
|
Expr::Async { body } => {
|
||||||
// Use the first type parameter as the output type of future.
|
// Use the first type parameter as the output type of future.
|
||||||
// existenail type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
|
// existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
|
||||||
let inner_ty = self.infer_expr(*body, &Expectation::none());
|
let inner_ty = self.infer_expr(*body, &Expectation::none());
|
||||||
let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
|
let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
|
||||||
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
|
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
|
||||||
|
@ -195,18 +202,16 @@ impl<'a> InferenceContext<'a> {
|
||||||
Expr::Loop { body, label } => {
|
Expr::Loop { body, label } => {
|
||||||
self.breakables.push(BreakableContext {
|
self.breakables.push(BreakableContext {
|
||||||
may_break: false,
|
may_break: false,
|
||||||
break_ty: self.table.new_type_var(),
|
coerce: CoerceMany::new(self.table.new_type_var()),
|
||||||
label: label.map(|label| self.body[label].name.clone()),
|
label: label.map(|label| self.body[label].name.clone()),
|
||||||
});
|
});
|
||||||
self.infer_expr(*body, &Expectation::has_type(TyBuilder::unit()));
|
self.infer_expr(*body, &Expectation::has_type(TyBuilder::unit()));
|
||||||
|
|
||||||
let ctxt = self.breakables.pop().expect("breakable stack broken");
|
let ctxt = self.breakables.pop().expect("breakable stack broken");
|
||||||
if ctxt.may_break {
|
|
||||||
self.diverges = Diverges::Maybe;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctxt.may_break {
|
if ctxt.may_break {
|
||||||
ctxt.break_ty
|
self.diverges = Diverges::Maybe;
|
||||||
|
ctxt.coerce.complete()
|
||||||
} else {
|
} else {
|
||||||
TyKind::Never.intern(&Interner)
|
TyKind::Never.intern(&Interner)
|
||||||
}
|
}
|
||||||
|
@ -214,7 +219,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
Expr::While { condition, body, label } => {
|
Expr::While { condition, body, label } => {
|
||||||
self.breakables.push(BreakableContext {
|
self.breakables.push(BreakableContext {
|
||||||
may_break: false,
|
may_break: false,
|
||||||
break_ty: self.err_ty(),
|
coerce: CoerceMany::new(self.err_ty()),
|
||||||
label: label.map(|label| self.body[label].name.clone()),
|
label: label.map(|label| self.body[label].name.clone()),
|
||||||
});
|
});
|
||||||
// while let is desugared to a match loop, so this is always simple while
|
// while let is desugared to a match loop, so this is always simple while
|
||||||
|
@ -233,7 +238,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
self.breakables.push(BreakableContext {
|
self.breakables.push(BreakableContext {
|
||||||
may_break: false,
|
may_break: false,
|
||||||
break_ty: self.err_ty(),
|
coerce: CoerceMany::new(self.err_ty()),
|
||||||
label: label.map(|label| self.body[label].name.clone()),
|
label: label.map(|label| self.body[label].name.clone()),
|
||||||
});
|
});
|
||||||
let pat_ty =
|
let pat_ty =
|
||||||
|
@ -284,7 +289,12 @@ impl<'a> InferenceContext<'a> {
|
||||||
// Eagerly try to relate the closure type with the expected
|
// Eagerly try to relate the closure type with the expected
|
||||||
// type, otherwise we often won't have enough information to
|
// type, otherwise we often won't have enough information to
|
||||||
// infer the body.
|
// infer the body.
|
||||||
self.deduce_closure_type_from_expectations(&closure_ty, &sig_ty, expected);
|
self.deduce_closure_type_from_expectations(
|
||||||
|
tgt_expr,
|
||||||
|
&closure_ty,
|
||||||
|
&sig_ty,
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
|
||||||
// Now go through the argument patterns
|
// Now go through the argument patterns
|
||||||
for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
|
for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
|
||||||
|
@ -331,7 +341,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
let expected = expected.adjust_for_branches(&mut self.table);
|
let expected = expected.adjust_for_branches(&mut self.table);
|
||||||
|
|
||||||
let mut result_ty = if arms.is_empty() {
|
let result_ty = if arms.is_empty() {
|
||||||
TyKind::Never.intern(&Interner)
|
TyKind::Never.intern(&Interner)
|
||||||
} else {
|
} else {
|
||||||
match &expected {
|
match &expected {
|
||||||
|
@ -339,6 +349,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
_ => self.table.new_type_var(),
|
_ => self.table.new_type_var(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let mut coerce = CoerceMany::new(result_ty);
|
||||||
|
|
||||||
let matchee_diverges = self.diverges;
|
let matchee_diverges = self.diverges;
|
||||||
let mut all_arms_diverge = Diverges::Always;
|
let mut all_arms_diverge = Diverges::Always;
|
||||||
|
@ -355,12 +366,12 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
||||||
all_arms_diverge &= self.diverges;
|
all_arms_diverge &= self.diverges;
|
||||||
result_ty = self.coerce_merge_branch(Some(arm.expr), &result_ty, &arm_ty);
|
coerce.coerce(self, Some(arm.expr), &arm_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.diverges = matchee_diverges | all_arms_diverge;
|
self.diverges = matchee_diverges | all_arms_diverge;
|
||||||
|
|
||||||
result_ty
|
coerce.complete()
|
||||||
}
|
}
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
// FIXME this could be more efficient...
|
// FIXME this could be more efficient...
|
||||||
|
@ -369,30 +380,35 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
Expr::Continue { .. } => TyKind::Never.intern(&Interner),
|
Expr::Continue { .. } => TyKind::Never.intern(&Interner),
|
||||||
Expr::Break { expr, label } => {
|
Expr::Break { expr, label } => {
|
||||||
let last_ty =
|
let mut coerce = match find_breakable(&mut self.breakables, label.as_ref()) {
|
||||||
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
|
Some(ctxt) => {
|
||||||
ctxt.break_ty.clone()
|
// avoiding the borrowck
|
||||||
} else {
|
mem::replace(
|
||||||
self.err_ty()
|
&mut ctxt.coerce,
|
||||||
};
|
CoerceMany::new(self.result.standard_types.unknown.clone()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => CoerceMany::new(self.result.standard_types.unknown.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
let val_ty = if let Some(expr) = expr {
|
let val_ty = if let Some(expr) = *expr {
|
||||||
self.infer_expr(*expr, &Expectation::none())
|
self.infer_expr(expr, &Expectation::none())
|
||||||
} else {
|
} else {
|
||||||
TyBuilder::unit()
|
TyBuilder::unit()
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: create a synthetic `()` during lowering so we have something to refer to here?
|
// FIXME: create a synthetic `()` during lowering so we have something to refer to here?
|
||||||
let merged_type = self.coerce_merge_branch(*expr, &last_ty, &val_ty);
|
coerce.coerce(self, *expr, &val_ty);
|
||||||
|
|
||||||
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
|
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
|
||||||
ctxt.break_ty = merged_type;
|
ctxt.coerce = coerce;
|
||||||
ctxt.may_break = true;
|
ctxt.may_break = true;
|
||||||
} else {
|
} else {
|
||||||
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
||||||
expr: tgt_expr,
|
expr: tgt_expr,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
TyKind::Never.intern(&Interner)
|
TyKind::Never.intern(&Interner)
|
||||||
}
|
}
|
||||||
Expr::Return { expr } => {
|
Expr::Return { expr } => {
|
||||||
|
@ -400,7 +416,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone()));
|
self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone()));
|
||||||
} else {
|
} else {
|
||||||
let unit = TyBuilder::unit();
|
let unit = TyBuilder::unit();
|
||||||
self.coerce(&unit, &self.return_ty.clone());
|
let _ = self.coerce(Some(tgt_expr), &unit, &self.return_ty.clone());
|
||||||
}
|
}
|
||||||
TyKind::Never.intern(&Interner)
|
TyKind::Never.intern(&Interner)
|
||||||
}
|
}
|
||||||
|
@ -729,39 +745,37 @@ impl<'a> InferenceContext<'a> {
|
||||||
TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner)
|
TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner)
|
||||||
}
|
}
|
||||||
Expr::Array(array) => {
|
Expr::Array(array) => {
|
||||||
let mut elem_ty =
|
let elem_ty =
|
||||||
match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(&Interner)) {
|
match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(&Interner)) {
|
||||||
Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(),
|
Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(),
|
||||||
_ => self.table.new_type_var(),
|
_ => self.table.new_type_var(),
|
||||||
};
|
};
|
||||||
|
let mut coerce = CoerceMany::new(elem_ty.clone());
|
||||||
|
|
||||||
let expected = Expectation::has_type(elem_ty.clone());
|
let expected = Expectation::has_type(elem_ty.clone());
|
||||||
let len = match array {
|
let len = match array {
|
||||||
Array::ElementList(items) => {
|
Array::ElementList(items) => {
|
||||||
for expr in items.iter() {
|
for &expr in items.iter() {
|
||||||
let cur_elem_ty = self.infer_expr_inner(*expr, &expected);
|
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
|
||||||
elem_ty = self.coerce_merge_branch(Some(*expr), &elem_ty, &cur_elem_ty);
|
coerce.coerce(self, Some(expr), &cur_elem_ty);
|
||||||
}
|
}
|
||||||
Some(items.len() as u64)
|
Some(items.len() as u64)
|
||||||
}
|
}
|
||||||
Array::Repeat { initializer, repeat } => {
|
&Array::Repeat { initializer, repeat } => {
|
||||||
self.infer_expr_coerce(
|
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
|
||||||
*initializer,
|
|
||||||
&Expectation::has_type(elem_ty.clone()),
|
|
||||||
);
|
|
||||||
self.infer_expr(
|
self.infer_expr(
|
||||||
*repeat,
|
repeat,
|
||||||
&Expectation::has_type(
|
&Expectation::has_type(
|
||||||
TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
|
TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let repeat_expr = &self.body.exprs[*repeat];
|
let repeat_expr = &self.body.exprs[repeat];
|
||||||
consteval::eval_usize(repeat_expr)
|
consteval::eval_usize(repeat_expr)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner)
|
TyKind::Array(coerce.complete(), consteval::usize_const(len)).intern(&Interner)
|
||||||
}
|
}
|
||||||
Expr::Literal(lit) => match lit {
|
Expr::Literal(lit) => match lit {
|
||||||
Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
|
Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
|
||||||
|
@ -810,6 +824,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
fn infer_block(
|
fn infer_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
expr: ExprId,
|
||||||
statements: &[Statement],
|
statements: &[Statement],
|
||||||
tail: Option<ExprId>,
|
tail: Option<ExprId>,
|
||||||
expected: &Expectation,
|
expected: &Expectation,
|
||||||
|
@ -856,7 +871,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.table.new_maybe_never_var()
|
self.table.new_maybe_never_var()
|
||||||
} else {
|
} else {
|
||||||
if let Some(t) = expected.only_has_type(&mut self.table) {
|
if let Some(t) = expected.only_has_type(&mut self.table) {
|
||||||
self.coerce(&TyBuilder::unit(), &t);
|
let _ = self.coerce(Some(expr), &TyBuilder::unit(), &t);
|
||||||
}
|
}
|
||||||
TyBuilder::unit()
|
TyBuilder::unit()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,9 @@ use hir_expand::name::Name;
|
||||||
|
|
||||||
use super::{BindingMode, Expectation, InferenceContext, TypeMismatch};
|
use super::{BindingMode, Expectation, InferenceContext, TypeMismatch};
|
||||||
use crate::{
|
use crate::{
|
||||||
lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder,
|
infer::{Adjust, Adjustment, AutoBorrow},
|
||||||
TyExt, TyKind,
|
lower::lower_to_chalk_mutability,
|
||||||
|
static_lifetime, Interner, Substitution, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
|
@ -103,7 +104,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
if is_non_ref_pat(&body, pat) {
|
if is_non_ref_pat(&body, pat) {
|
||||||
let mut pat_adjustments = Vec::new();
|
let mut pat_adjustments = Vec::new();
|
||||||
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
|
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
|
||||||
pat_adjustments.push(expected.clone());
|
pat_adjustments.push(Adjustment {
|
||||||
|
target: expected.clone(),
|
||||||
|
kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
|
||||||
|
});
|
||||||
expected = self.resolve_ty_shallow(inner);
|
expected = self.resolve_ty_shallow(inner);
|
||||||
default_bm = match default_bm {
|
default_bm = match default_bm {
|
||||||
BindingMode::Move => BindingMode::Ref(mutability),
|
BindingMode::Move => BindingMode::Ref(mutability),
|
||||||
|
|
|
@ -315,7 +315,7 @@ impl<'a> InferenceTable<'a> {
|
||||||
|
|
||||||
/// Unify two types and return new trait goals arising from it, so the
|
/// Unify two types and return new trait goals arising from it, so the
|
||||||
/// caller needs to deal with them.
|
/// caller needs to deal with them.
|
||||||
pub(crate) fn try_unify<T: Zip<Interner>>(&mut self, t1: &T, t2: &T) -> InferResult {
|
pub(crate) fn try_unify<T: Zip<Interner>>(&mut self, t1: &T, t2: &T) -> InferResult<()> {
|
||||||
match self.var_unification_table.relate(
|
match self.var_unification_table.relate(
|
||||||
&Interner,
|
&Interner,
|
||||||
&self.db,
|
&self.db,
|
||||||
|
@ -324,7 +324,7 @@ impl<'a> InferenceTable<'a> {
|
||||||
t1,
|
t1,
|
||||||
t2,
|
t2,
|
||||||
) {
|
) {
|
||||||
Ok(result) => Ok(InferOk { goals: result.goals }),
|
Ok(result) => Ok(InferOk { goals: result.goals, value: () }),
|
||||||
Err(chalk_ir::NoSolution) => Err(TypeError),
|
Err(chalk_ir::NoSolution) => Err(TypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,7 @@ impl<'a> InferenceTable<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk) {
|
pub(crate) fn register_infer_ok<T>(&mut self, infer_ok: InferOk<T>) {
|
||||||
infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal));
|
infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,11 @@ use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
|
||||||
use tracing_tree::HierarchicalLayer;
|
use tracing_tree::HierarchicalLayer;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty,
|
db::HirDatabase,
|
||||||
|
display::HirDisplay,
|
||||||
|
infer::{Adjustment, TypeMismatch},
|
||||||
|
test_db::TestDB,
|
||||||
|
InferenceResult, Ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
// These tests compare the inference results for all expressions in a file
|
// These tests compare the inference results for all expressions in a file
|
||||||
|
@ -79,6 +83,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||||
let mut had_annotations = false;
|
let mut had_annotations = false;
|
||||||
let mut mismatches = HashMap::new();
|
let mut mismatches = HashMap::new();
|
||||||
let mut types = HashMap::new();
|
let mut types = HashMap::new();
|
||||||
|
let mut adjustments = HashMap::<_, Vec<_>>::new();
|
||||||
for (file_id, annotations) in db.extract_annotations() {
|
for (file_id, annotations) in db.extract_annotations() {
|
||||||
for (range, expected) in annotations {
|
for (range, expected) in annotations {
|
||||||
let file_range = FileRange { file_id, range };
|
let file_range = FileRange { file_id, range };
|
||||||
|
@ -88,6 +93,15 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||||
types.insert(file_range, expected.trim_start_matches("type: ").to_string());
|
types.insert(file_range, expected.trim_start_matches("type: ").to_string());
|
||||||
} else if expected.starts_with("expected") {
|
} else if expected.starts_with("expected") {
|
||||||
mismatches.insert(file_range, expected);
|
mismatches.insert(file_range, expected);
|
||||||
|
} else if expected.starts_with("adjustments: ") {
|
||||||
|
adjustments.insert(
|
||||||
|
file_range,
|
||||||
|
expected
|
||||||
|
.trim_start_matches("adjustments: ")
|
||||||
|
.split(',')
|
||||||
|
.map(|it| it.trim().to_string())
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected annotation: {}", expected);
|
panic!("unexpected annotation: {}", expected);
|
||||||
}
|
}
|
||||||
|
@ -155,6 +169,19 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||||
};
|
};
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
if let Some(expected) = adjustments.remove(&range) {
|
||||||
|
if let Some(adjustments) = inference_result.expr_adjustments.get(&expr) {
|
||||||
|
assert_eq!(
|
||||||
|
expected,
|
||||||
|
adjustments
|
||||||
|
.iter()
|
||||||
|
.map(|Adjustment { kind, .. }| format!("{:?}", kind))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("expected {:?} adjustments, found none", expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pat, mismatch) in inference_result.pat_type_mismatches() {
|
for (pat, mismatch) in inference_result.pat_type_mismatches() {
|
||||||
|
@ -212,6 +239,12 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||||
format_to!(buf, "{:?}: type {}\n", t.0.range, t.1);
|
format_to!(buf, "{:?}: type {}\n", t.0.range, t.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !adjustments.is_empty() {
|
||||||
|
format_to!(buf, "Unchecked adjustments annotations:\n");
|
||||||
|
for t in adjustments {
|
||||||
|
format_to!(buf, "{:?}: type {:?}\n", t.0.range, t.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
assert!(buf.is_empty(), "{}", buf);
|
assert!(buf.is_empty(), "{}", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,9 @@ fn let_stmt_coerce() {
|
||||||
//- minicore: coerce_unsized
|
//- minicore: coerce_unsized
|
||||||
fn test() {
|
fn test() {
|
||||||
let x: &[isize] = &[1];
|
let x: &[isize] = &[1];
|
||||||
|
// ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
|
||||||
let x: *const [isize] = &[1];
|
let x: *const [isize] = &[1];
|
||||||
|
// ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize)
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
|
@ -96,6 +98,7 @@ fn foo<T>(x: &[T]) -> &[T] { x }
|
||||||
fn test() {
|
fn test() {
|
||||||
let x = if true {
|
let x = if true {
|
||||||
foo(&[1])
|
foo(&[1])
|
||||||
|
// ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
|
||||||
} else {
|
} else {
|
||||||
&[1]
|
&[1]
|
||||||
};
|
};
|
||||||
|
@ -130,6 +133,7 @@ fn foo<T>(x: &[T]) -> &[T] { x }
|
||||||
fn test(i: i32) {
|
fn test(i: i32) {
|
||||||
let x = match i {
|
let x = match i {
|
||||||
2 => foo(&[2]),
|
2 => foo(&[2]),
|
||||||
|
// ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
|
||||||
1 => &[1],
|
1 => &[1],
|
||||||
_ => &[3],
|
_ => &[3],
|
||||||
};
|
};
|
||||||
|
@ -144,6 +148,7 @@ fn match_second_coerce() {
|
||||||
r#"
|
r#"
|
||||||
//- minicore: coerce_unsized
|
//- minicore: coerce_unsized
|
||||||
fn foo<T>(x: &[T]) -> &[T] { loop {} }
|
fn foo<T>(x: &[T]) -> &[T] { loop {} }
|
||||||
|
// ^^^^^^^ adjustments: NeverToAny
|
||||||
fn test(i: i32) {
|
fn test(i: i32) {
|
||||||
let x = match i {
|
let x = match i {
|
||||||
1 => &[1],
|
1 => &[1],
|
||||||
|
@ -168,9 +173,12 @@ fn test() {
|
||||||
2 => t as &i32,
|
2 => t as &i32,
|
||||||
//^^^^^^^^^ expected *mut i32, got &i32
|
//^^^^^^^^^ expected *mut i32, got &i32
|
||||||
_ => t as *const i32,
|
_ => t as *const i32,
|
||||||
|
// ^^^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer)
|
||||||
|
|
||||||
};
|
};
|
||||||
x;
|
x;
|
||||||
//^ type: *const i32
|
//^ type: *const i32
|
||||||
|
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
|
@ -255,6 +263,9 @@ fn coerce_fn_item_to_fn_ptr() {
|
||||||
fn foo(x: u32) -> isize { 1 }
|
fn foo(x: u32) -> isize { 1 }
|
||||||
fn test() {
|
fn test() {
|
||||||
let f: fn(u32) -> isize = foo;
|
let f: fn(u32) -> isize = foo;
|
||||||
|
// ^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
|
let f: unsafe fn(u32) -> isize = foo;
|
||||||
|
// ^^^ adjustments: Pointer(ReifyFnPointer)
|
||||||
}",
|
}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue