mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Auto merge of #18278 - ShoyuVanilla:never-place, r=Veykril
Do not consider match/let/ref of place that evaluates to ! to diverge, disallow coercions from them too Resolves #18237
This commit is contained in:
commit
418c1365ec
8 changed files with 637 additions and 102 deletions
|
@ -57,7 +57,7 @@ use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
fold_tys,
|
fold_tys,
|
||||||
generics::Generics,
|
generics::Generics,
|
||||||
infer::{coerce::CoerceMany, unify::InferenceTable},
|
infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable},
|
||||||
lower::ImplTraitLoweringMode,
|
lower::ImplTraitLoweringMode,
|
||||||
mir::MirSpan,
|
mir::MirSpan,
|
||||||
to_assoc_type_id,
|
to_assoc_type_id,
|
||||||
|
@ -1154,6 +1154,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
_ = self.infer_expr_coerce(
|
_ = self.infer_expr_coerce(
|
||||||
self.body.body_expr,
|
self.body.body_expr,
|
||||||
&Expectation::has_type(self.return_ty.clone()),
|
&Expectation::has_type(self.return_ty.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,9 @@ use hir_def::{hir::ExprId, AdtId};
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
infer::unify::InferenceTable, Adjustment, Binders, DynTy, InferenceDiagnostic, Interner,
|
infer::{coerce::CoerceNever, unify::InferenceTable},
|
||||||
PlaceholderIndex, QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause,
|
Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex,
|
||||||
|
QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -128,7 +129,7 @@ impl CastCheck {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) {
|
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) {
|
||||||
apply_adjustments(self.source_expr, adj);
|
apply_adjustments(self.source_expr, adj);
|
||||||
set_coercion_cast(self.source_expr);
|
set_coercion_cast(self.source_expr);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -154,7 +155,8 @@ impl CastCheck {
|
||||||
let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig");
|
let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig");
|
||||||
let sig = table.normalize_associated_types_in(sig);
|
let sig = table.normalize_associated_types_in(sig);
|
||||||
let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
|
let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
|
||||||
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr) {
|
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes)
|
||||||
|
{
|
||||||
apply_adjustments(self.source_expr, adj);
|
apply_adjustments(self.source_expr, adj);
|
||||||
} else {
|
} else {
|
||||||
return Err(CastError::IllegalCast);
|
return Err(CastError::IllegalCast);
|
||||||
|
@ -241,7 +243,8 @@ impl CastCheck {
|
||||||
if let TyKind::Array(ety, _) = t_expr.kind(Interner) {
|
if let TyKind::Array(ety, _) = t_expr.kind(Interner) {
|
||||||
// Coerce to a raw pointer so that we generate RawPtr in MIR.
|
// Coerce to a raw pointer so that we generate RawPtr in MIR.
|
||||||
let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner);
|
let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner);
|
||||||
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type) {
|
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, CoerceNever::Yes)
|
||||||
|
{
|
||||||
apply_adjustments(self.source_expr, adj);
|
apply_adjustments(self.source_expr, adj);
|
||||||
} else {
|
} else {
|
||||||
never!(
|
never!(
|
||||||
|
@ -253,7 +256,7 @@ impl CastCheck {
|
||||||
|
|
||||||
// This is a less strict condition than rustc's `demand_eqtype`,
|
// This is a less strict condition than rustc's `demand_eqtype`,
|
||||||
// but false negative is better than false positive
|
// but false negative is better than false positive
|
||||||
if table.coerce(ety, t_cast).is_ok() {
|
if table.coerce(ety, t_cast, CoerceNever::Yes).is_ok() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ use crate::{
|
||||||
db::{HirDatabase, InternedClosure},
|
db::{HirDatabase, InternedClosure},
|
||||||
error_lifetime, from_chalk_trait_id, from_placeholder_idx,
|
error_lifetime, from_chalk_trait_id, from_placeholder_idx,
|
||||||
generics::Generics,
|
generics::Generics,
|
||||||
|
infer::coerce::CoerceNever,
|
||||||
make_binders,
|
make_binders,
|
||||||
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
|
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
|
||||||
to_chalk_trait_id,
|
to_chalk_trait_id,
|
||||||
|
@ -65,7 +66,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.
|
||||||
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty);
|
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty, CoerceNever::Yes);
|
||||||
|
|
||||||
// Coroutines are not Fn* so return early.
|
// Coroutines are not Fn* so return early.
|
||||||
if matches!(closure_ty.kind(Interner), TyKind::Coroutine(..)) {
|
if matches!(closure_ty.kind(Interner), TyKind::Coroutine(..)) {
|
||||||
|
|
|
@ -139,8 +139,8 @@ impl CoerceMany {
|
||||||
};
|
};
|
||||||
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 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
|
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty, CoerceNever::Yes);
|
||||||
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
|
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty, CoerceNever::Yes);
|
||||||
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
||||||
ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
|
ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
|
||||||
for &e in &self.expressions {
|
for &e in &self.expressions {
|
||||||
|
@ -159,9 +159,9 @@ impl CoerceMany {
|
||||||
// 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 let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty()) {
|
if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) {
|
||||||
self.final_ty = Some(res);
|
self.final_ty = Some(res);
|
||||||
} else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
|
} else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty, CoerceNever::Yes) {
|
||||||
self.final_ty = Some(res);
|
self.final_ty = Some(res);
|
||||||
} else {
|
} else {
|
||||||
match cause {
|
match cause {
|
||||||
|
@ -197,7 +197,7 @@ pub(crate) fn coerce(
|
||||||
let vars = table.fresh_subst(tys.binders.as_slice(Interner));
|
let vars = table.fresh_subst(tys.binders.as_slice(Interner));
|
||||||
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
|
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
|
||||||
let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
|
let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
|
||||||
let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars)?;
|
let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars, CoerceNever::Yes)?;
|
||||||
// default any type vars that weren't unified back to their original bound vars
|
// default any type vars that weren't unified back to their original bound vars
|
||||||
// (kind of hacky)
|
// (kind of hacky)
|
||||||
let find_var = |iv| {
|
let find_var = |iv| {
|
||||||
|
@ -219,6 +219,12 @@ pub(crate) fn coerce(
|
||||||
Ok((adjustments, table.resolve_with_fallback(ty, &fallback)))
|
Ok((adjustments, table.resolve_with_fallback(ty, &fallback)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum CoerceNever {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
impl InferenceContext<'_> {
|
impl InferenceContext<'_> {
|
||||||
/// Unify two types, but may coerce the first one to the second one
|
/// Unify two types, but may coerce the first one to the second one
|
||||||
/// using "implicit coercion rules" if needed.
|
/// using "implicit coercion rules" if needed.
|
||||||
|
@ -227,10 +233,16 @@ impl InferenceContext<'_> {
|
||||||
expr: Option<ExprId>,
|
expr: Option<ExprId>,
|
||||||
from_ty: &Ty,
|
from_ty: &Ty,
|
||||||
to_ty: &Ty,
|
to_ty: &Ty,
|
||||||
|
// [Comment from rustc](https://github.com/rust-lang/rust/blob/4cc494bbfe9911d24f3ee521f98d5c6bb7e3ffe8/compiler/rustc_hir_typeck/src/coercion.rs#L85-L89)
|
||||||
|
// Whether we allow `NeverToAny` coercions. This is unsound if we're
|
||||||
|
// coercing a place expression without it counting as a read in the MIR.
|
||||||
|
// This is a side-effect of HIR not really having a great distinction
|
||||||
|
// between places and values.
|
||||||
|
coerce_never: CoerceNever,
|
||||||
) -> Result<Ty, TypeError> {
|
) -> Result<Ty, TypeError> {
|
||||||
let from_ty = self.resolve_ty_shallow(from_ty);
|
let from_ty = self.resolve_ty_shallow(from_ty);
|
||||||
let to_ty = self.resolve_ty_shallow(to_ty);
|
let to_ty = self.resolve_ty_shallow(to_ty);
|
||||||
let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty)?;
|
let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty, coerce_never)?;
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
self.write_expr_adj(expr, adjustments);
|
self.write_expr_adj(expr, adjustments);
|
||||||
}
|
}
|
||||||
|
@ -245,10 +257,11 @@ impl InferenceTable<'_> {
|
||||||
&mut self,
|
&mut self,
|
||||||
from_ty: &Ty,
|
from_ty: &Ty,
|
||||||
to_ty: &Ty,
|
to_ty: &Ty,
|
||||||
|
coerce_never: CoerceNever,
|
||||||
) -> Result<(Vec<Adjustment>, Ty), TypeError> {
|
) -> Result<(Vec<Adjustment>, Ty), TypeError> {
|
||||||
let from_ty = self.resolve_ty_shallow(from_ty);
|
let from_ty = self.resolve_ty_shallow(from_ty);
|
||||||
let to_ty = self.resolve_ty_shallow(to_ty);
|
let to_ty = self.resolve_ty_shallow(to_ty);
|
||||||
match self.coerce_inner(from_ty, &to_ty) {
|
match self.coerce_inner(from_ty, &to_ty, coerce_never) {
|
||||||
Ok(InferOk { value: (adjustments, ty), goals }) => {
|
Ok(InferOk { value: (adjustments, ty), goals }) => {
|
||||||
self.register_infer_ok(InferOk { value: (), goals });
|
self.register_infer_ok(InferOk { value: (), goals });
|
||||||
Ok((adjustments, ty))
|
Ok((adjustments, ty))
|
||||||
|
@ -260,8 +273,12 @@ impl InferenceTable<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult {
|
fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: CoerceNever) -> CoerceResult {
|
||||||
if from_ty.is_never() {
|
if from_ty.is_never() {
|
||||||
|
if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) {
|
||||||
|
self.set_diverging(*tv, true);
|
||||||
|
}
|
||||||
|
if coerce_never == CoerceNever::Yes {
|
||||||
// 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
|
||||||
// otherwise constrained. An example where this arises:
|
// otherwise constrained. An example where this arises:
|
||||||
|
@ -269,10 +286,10 @@ impl InferenceTable<'_> {
|
||||||
// let _: Option<?T> = Some({ return; });
|
// let _: Option<?T> = Some({ return; });
|
||||||
//
|
//
|
||||||
// here, we would coerce from `!` to `?T`.
|
// here, we would coerce from `!` to `?T`.
|
||||||
if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) {
|
|
||||||
self.set_diverging(*tv, true);
|
|
||||||
}
|
|
||||||
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
|
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
|
||||||
|
} else {
|
||||||
|
return self.unify_and(&from_ty, to_ty, identity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are coercing into a TAIT, coerce into its proxy inference var, instead.
|
// If we are coercing into a TAIT, coerce into its proxy inference var, instead.
|
||||||
|
|
|
@ -10,10 +10,11 @@ use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
hir::{
|
hir::{
|
||||||
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
|
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
|
||||||
Literal, Statement, UnaryOp,
|
Literal, Pat, PatId, Statement, UnaryOp,
|
||||||
},
|
},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
path::{GenericArg, GenericArgs, Path},
|
path::{GenericArg, GenericArgs, Path},
|
||||||
|
resolver::ValueNs,
|
||||||
BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
|
BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
|
@ -28,7 +29,7 @@ use crate::{
|
||||||
error_lifetime,
|
error_lifetime,
|
||||||
generics::{generics, Generics},
|
generics::{generics, Generics},
|
||||||
infer::{
|
infer::{
|
||||||
coerce::{CoerceMany, CoercionCause},
|
coerce::{CoerceMany, CoerceNever, CoercionCause},
|
||||||
find_continuable,
|
find_continuable,
|
||||||
pat::contains_explicit_ref_binding,
|
pat::contains_explicit_ref_binding,
|
||||||
BreakableKind,
|
BreakableKind,
|
||||||
|
@ -52,9 +53,20 @@ use super::{
|
||||||
Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
|
Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum ExprIsRead {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
impl InferenceContext<'_> {
|
impl InferenceContext<'_> {
|
||||||
pub(crate) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
pub(crate) fn infer_expr(
|
||||||
let ty = self.infer_expr_inner(tgt_expr, expected);
|
&mut self,
|
||||||
|
tgt_expr: ExprId,
|
||||||
|
expected: &Expectation,
|
||||||
|
is_read: ExprIsRead,
|
||||||
|
) -> Ty {
|
||||||
|
let ty = self.infer_expr_inner(tgt_expr, expected, is_read);
|
||||||
if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
|
if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
|
||||||
let could_unify = self.unify(&ty, &expected_ty);
|
let could_unify = self.unify(&ty, &expected_ty);
|
||||||
if !could_unify {
|
if !could_unify {
|
||||||
|
@ -67,16 +79,26 @@ impl InferenceContext<'_> {
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId) -> Ty {
|
pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId, is_read: ExprIsRead) -> Ty {
|
||||||
self.infer_expr_inner(tgt_expr, &Expectation::None)
|
self.infer_expr_inner(tgt_expr, &Expectation::None, is_read)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Infer type of expression with possibly implicit coerce to the expected type.
|
/// Infer type of expression with possibly implicit coerce to the expected type.
|
||||||
/// Return the type after possible coercion.
|
/// Return the type after possible coercion.
|
||||||
pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
pub(super) fn infer_expr_coerce(
|
||||||
let ty = self.infer_expr_inner(expr, expected);
|
&mut self,
|
||||||
|
expr: ExprId,
|
||||||
|
expected: &Expectation,
|
||||||
|
is_read: ExprIsRead,
|
||||||
|
) -> Ty {
|
||||||
|
let ty = self.infer_expr_inner(expr, expected, is_read);
|
||||||
if let Some(target) = expected.only_has_type(&mut self.table) {
|
if let Some(target) = expected.only_has_type(&mut self.table) {
|
||||||
match self.coerce(Some(expr), &ty, &target) {
|
let coerce_never = if self.expr_guaranteed_to_constitute_read_for_never(expr, is_read) {
|
||||||
|
CoerceNever::Yes
|
||||||
|
} else {
|
||||||
|
CoerceNever::No
|
||||||
|
};
|
||||||
|
match self.coerce(Some(expr), &ty, &target, coerce_never) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
self.result.type_mismatches.insert(
|
self.result.type_mismatches.insert(
|
||||||
|
@ -91,8 +113,137 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
/// Whether this expression constitutes a read of value of the type that
|
||||||
let ty = self.infer_expr_inner(expr, expected);
|
/// it evaluates to.
|
||||||
|
///
|
||||||
|
/// This is used to determine if we should consider the block to diverge
|
||||||
|
/// if the expression evaluates to `!`, and if we should insert a `NeverToAny`
|
||||||
|
/// coercion for values of type `!`.
|
||||||
|
///
|
||||||
|
/// This function generally returns `false` if the expression is a place
|
||||||
|
/// expression and the *parent* expression is the scrutinee of a match or
|
||||||
|
/// the pointee of an `&` addr-of expression, since both of those parent
|
||||||
|
/// expressions take a *place* and not a value.
|
||||||
|
pub(super) fn expr_guaranteed_to_constitute_read_for_never(
|
||||||
|
&mut self,
|
||||||
|
expr: ExprId,
|
||||||
|
is_read: ExprIsRead,
|
||||||
|
) -> bool {
|
||||||
|
// rustc does the place expr check first, but since we are feeding
|
||||||
|
// readness of the `expr` as a given value, we just can short-circuit
|
||||||
|
// the place expr check if it's true(see codes and comments below)
|
||||||
|
if is_read == ExprIsRead::Yes {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only care about place exprs. Anything else returns an immediate
|
||||||
|
// which would constitute a read. We don't care about distinguishing
|
||||||
|
// "syntactic" place exprs since if the base of a field projection is
|
||||||
|
// not a place then it would've been UB to read from it anyways since
|
||||||
|
// that constitutes a read.
|
||||||
|
if !self.is_syntactic_place_expr(expr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rustc queries parent hir node of `expr` here and determine whether
|
||||||
|
// the current `expr` is read of value per its parent.
|
||||||
|
// But since we don't have hir node, we cannot follow such "bottom-up"
|
||||||
|
// method.
|
||||||
|
// So, we pass down such readness from the parent expression through the
|
||||||
|
// recursive `infer_expr*` calls in a "top-down" manner.
|
||||||
|
is_read == ExprIsRead::Yes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this pattern constitutes a read of value of the scrutinee that
|
||||||
|
/// it is matching against. This is used to determine whether we should
|
||||||
|
/// perform `NeverToAny` coercions.
|
||||||
|
fn pat_guaranteed_to_constitute_read_for_never(&self, pat: PatId) -> bool {
|
||||||
|
match &self.body[pat] {
|
||||||
|
// Does not constitute a read.
|
||||||
|
Pat::Wild => false,
|
||||||
|
|
||||||
|
// This is unnecessarily restrictive when the pattern that doesn't
|
||||||
|
// constitute a read is unreachable.
|
||||||
|
//
|
||||||
|
// For example `match *never_ptr { value => {}, _ => {} }` or
|
||||||
|
// `match *never_ptr { _ if false => {}, value => {} }`.
|
||||||
|
//
|
||||||
|
// It is however fine to be restrictive here; only returning `true`
|
||||||
|
// can lead to unsoundness.
|
||||||
|
Pat::Or(subpats) => {
|
||||||
|
subpats.iter().all(|pat| self.pat_guaranteed_to_constitute_read_for_never(*pat))
|
||||||
|
}
|
||||||
|
|
||||||
|
// All of these constitute a read, or match on something that isn't `!`,
|
||||||
|
// which would require a `NeverToAny` coercion.
|
||||||
|
Pat::Bind { .. }
|
||||||
|
| Pat::TupleStruct { .. }
|
||||||
|
| Pat::Path(_)
|
||||||
|
| Pat::Tuple { .. }
|
||||||
|
| Pat::Box { .. }
|
||||||
|
| Pat::Ref { .. }
|
||||||
|
| Pat::Lit(_)
|
||||||
|
| Pat::Range { .. }
|
||||||
|
| Pat::Slice { .. }
|
||||||
|
| Pat::ConstBlock(_)
|
||||||
|
| Pat::Record { .. }
|
||||||
|
| Pat::Missing => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_syntactic_place_expr(&self, expr: ExprId) -> bool {
|
||||||
|
match &self.body[expr] {
|
||||||
|
// Lang item paths cannot currently be local variables or statics.
|
||||||
|
Expr::Path(Path::LangItem(_, _)) => false,
|
||||||
|
Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false,
|
||||||
|
Expr::Path(path) => self
|
||||||
|
.resolver
|
||||||
|
.resolve_path_in_value_ns_fully(self.db.upcast(), path)
|
||||||
|
.map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))),
|
||||||
|
Expr::Underscore => true,
|
||||||
|
Expr::UnaryOp { op: UnaryOp::Deref, .. } => true,
|
||||||
|
Expr::Field { .. } | Expr::Index { .. } => true,
|
||||||
|
Expr::Call { .. }
|
||||||
|
| Expr::MethodCall { .. }
|
||||||
|
| Expr::Tuple { .. }
|
||||||
|
| Expr::If { .. }
|
||||||
|
| Expr::Match { .. }
|
||||||
|
| Expr::Closure { .. }
|
||||||
|
| Expr::Block { .. }
|
||||||
|
| Expr::Array(..)
|
||||||
|
| Expr::Break { .. }
|
||||||
|
| Expr::Continue { .. }
|
||||||
|
| Expr::Return { .. }
|
||||||
|
| Expr::Become { .. }
|
||||||
|
| Expr::Let { .. }
|
||||||
|
| Expr::Loop { .. }
|
||||||
|
| Expr::InlineAsm(..)
|
||||||
|
| Expr::OffsetOf(..)
|
||||||
|
| Expr::Literal(..)
|
||||||
|
| Expr::Const(..)
|
||||||
|
| Expr::UnaryOp { .. }
|
||||||
|
| Expr::BinaryOp { .. }
|
||||||
|
| Expr::Yield { .. }
|
||||||
|
| Expr::Cast { .. }
|
||||||
|
| Expr::Async { .. }
|
||||||
|
| Expr::Unsafe { .. }
|
||||||
|
| Expr::Await { .. }
|
||||||
|
| Expr::Ref { .. }
|
||||||
|
| Expr::Range { .. }
|
||||||
|
| Expr::Box { .. }
|
||||||
|
| Expr::RecordLit { .. }
|
||||||
|
| Expr::Yeet { .. }
|
||||||
|
| Expr::Missing => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_expr_coerce_never(
|
||||||
|
&mut self,
|
||||||
|
expr: ExprId,
|
||||||
|
expected: &Expectation,
|
||||||
|
is_read: ExprIsRead,
|
||||||
|
) -> Ty {
|
||||||
|
let ty = self.infer_expr_inner(expr, expected, is_read);
|
||||||
// While we don't allow *arbitrary* coercions here, we *do* allow
|
// While we don't allow *arbitrary* coercions here, we *do* allow
|
||||||
// coercions from `!` to `expected`.
|
// coercions from `!` to `expected`.
|
||||||
if ty.is_never() {
|
if ty.is_never() {
|
||||||
|
@ -105,7 +256,7 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(target) = expected.only_has_type(&mut self.table) {
|
if let Some(target) = expected.only_has_type(&mut self.table) {
|
||||||
self.coerce(Some(expr), &ty, &target)
|
self.coerce(Some(expr), &ty, &target, CoerceNever::Yes)
|
||||||
.expect("never-to-any coercion should always succeed")
|
.expect("never-to-any coercion should always succeed")
|
||||||
} else {
|
} else {
|
||||||
ty
|
ty
|
||||||
|
@ -124,7 +275,12 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
fn infer_expr_inner(
|
||||||
|
&mut self,
|
||||||
|
tgt_expr: ExprId,
|
||||||
|
expected: &Expectation,
|
||||||
|
is_read: ExprIsRead,
|
||||||
|
) -> Ty {
|
||||||
self.db.unwind_if_cancelled();
|
self.db.unwind_if_cancelled();
|
||||||
|
|
||||||
let ty = match &self.body[tgt_expr] {
|
let ty = match &self.body[tgt_expr] {
|
||||||
|
@ -134,17 +290,18 @@ impl InferenceContext<'_> {
|
||||||
self.infer_expr_coerce_never(
|
self.infer_expr_coerce_never(
|
||||||
condition,
|
condition,
|
||||||
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
);
|
);
|
||||||
|
|
||||||
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
|
|
||||||
let then_ty = self.infer_expr_inner(then_branch, expected);
|
let then_ty = self.infer_expr_inner(then_branch, expected, ExprIsRead::Yes);
|
||||||
let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
|
let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
|
||||||
coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch));
|
coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch));
|
||||||
match else_branch {
|
match else_branch {
|
||||||
Some(else_branch) => {
|
Some(else_branch) => {
|
||||||
let else_ty = self.infer_expr_inner(else_branch, expected);
|
let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes);
|
||||||
let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
coerce.coerce(
|
coerce.coerce(
|
||||||
self,
|
self,
|
||||||
|
@ -163,7 +320,12 @@ impl InferenceContext<'_> {
|
||||||
coerce.complete(self)
|
coerce.complete(self)
|
||||||
}
|
}
|
||||||
&Expr::Let { pat, expr } => {
|
&Expr::Let { pat, expr } => {
|
||||||
let input_ty = self.infer_expr(expr, &Expectation::none());
|
let child_is_read = if self.pat_guaranteed_to_constitute_read_for_never(pat) {
|
||||||
|
ExprIsRead::Yes
|
||||||
|
} else {
|
||||||
|
ExprIsRead::No
|
||||||
|
};
|
||||||
|
let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read);
|
||||||
self.infer_top_pat(pat, &input_ty);
|
self.infer_top_pat(pat, &input_ty);
|
||||||
self.result.standard_types.bool_.clone()
|
self.result.standard_types.bool_.clone()
|
||||||
}
|
}
|
||||||
|
@ -176,7 +338,7 @@ impl InferenceContext<'_> {
|
||||||
Expr::Const(id) => {
|
Expr::Const(id) => {
|
||||||
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
|
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
|
||||||
let loc = this.db.lookup_intern_anonymous_const(*id);
|
let loc = this.db.lookup_intern_anonymous_const(*id);
|
||||||
this.infer_expr(loc.root, expected)
|
this.infer_expr(loc.root, expected, ExprIsRead::Yes)
|
||||||
})
|
})
|
||||||
.1
|
.1
|
||||||
}
|
}
|
||||||
|
@ -189,7 +351,11 @@ impl InferenceContext<'_> {
|
||||||
let ty = self.table.new_type_var();
|
let ty = self.table.new_type_var();
|
||||||
let (breaks, ()) =
|
let (breaks, ()) =
|
||||||
self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| {
|
self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| {
|
||||||
this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
|
this.infer_expr(
|
||||||
|
body,
|
||||||
|
&Expectation::HasType(TyBuilder::unit()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
match breaks {
|
match breaks {
|
||||||
|
@ -312,7 +478,7 @@ impl InferenceContext<'_> {
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
Expr::Call { callee, args, .. } => {
|
Expr::Call { callee, args, .. } => {
|
||||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
|
||||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
|
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
|
||||||
let (res, derefed_callee) = loop {
|
let (res, derefed_callee) = loop {
|
||||||
let Some((callee_deref_ty, _)) = derefs.next() else {
|
let Some((callee_deref_ty, _)) = derefs.next() else {
|
||||||
|
@ -393,7 +559,12 @@ impl InferenceContext<'_> {
|
||||||
expected,
|
expected,
|
||||||
),
|
),
|
||||||
Expr::Match { expr, arms } => {
|
Expr::Match { expr, arms } => {
|
||||||
let input_ty = self.infer_expr(*expr, &Expectation::none());
|
let scrutinee_is_read = arms
|
||||||
|
.iter()
|
||||||
|
.all(|arm| self.pat_guaranteed_to_constitute_read_for_never(arm.pat));
|
||||||
|
let scrutinee_is_read =
|
||||||
|
if scrutinee_is_read { ExprIsRead::Yes } else { ExprIsRead::No };
|
||||||
|
let input_ty = self.infer_expr(*expr, &Expectation::none(), scrutinee_is_read);
|
||||||
|
|
||||||
if arms.is_empty() {
|
if arms.is_empty() {
|
||||||
self.diverges = Diverges::Always;
|
self.diverges = Diverges::Always;
|
||||||
|
@ -423,11 +594,12 @@ impl InferenceContext<'_> {
|
||||||
self.infer_expr_coerce_never(
|
self.infer_expr_coerce_never(
|
||||||
guard_expr,
|
guard_expr,
|
||||||
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.diverges = Diverges::Maybe;
|
self.diverges = Diverges::Maybe;
|
||||||
|
|
||||||
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes);
|
||||||
all_arms_diverge &= self.diverges;
|
all_arms_diverge &= self.diverges;
|
||||||
coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr));
|
coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr));
|
||||||
}
|
}
|
||||||
|
@ -480,7 +652,11 @@ impl InferenceContext<'_> {
|
||||||
},
|
},
|
||||||
None => self.err_ty(),
|
None => self.err_ty(),
|
||||||
};
|
};
|
||||||
self.infer_expr_inner(expr, &Expectation::HasType(opt_coerce_to))
|
self.infer_expr_inner(
|
||||||
|
expr,
|
||||||
|
&Expectation::HasType(opt_coerce_to),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
TyBuilder::unit()
|
TyBuilder::unit()
|
||||||
};
|
};
|
||||||
|
@ -517,10 +693,14 @@ impl InferenceContext<'_> {
|
||||||
Expr::Yield { expr } => {
|
Expr::Yield { expr } => {
|
||||||
if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
|
if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
self.infer_expr_coerce(*expr, &Expectation::has_type(yield_ty));
|
self.infer_expr_coerce(
|
||||||
|
*expr,
|
||||||
|
&Expectation::has_type(yield_ty),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let unit = self.result.standard_types.unit.clone();
|
let unit = self.result.standard_types.unit.clone();
|
||||||
let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty);
|
let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty, CoerceNever::Yes);
|
||||||
}
|
}
|
||||||
resume_ty
|
resume_ty
|
||||||
} else {
|
} else {
|
||||||
|
@ -530,7 +710,7 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
Expr::Yeet { expr } => {
|
Expr::Yeet { expr } => {
|
||||||
if let &Some(expr) = expr {
|
if let &Some(expr) = expr {
|
||||||
self.infer_expr_no_expect(expr);
|
self.infer_expr_no_expect(expr, ExprIsRead::Yes);
|
||||||
}
|
}
|
||||||
self.result.standard_types.never.clone()
|
self.result.standard_types.never.clone()
|
||||||
}
|
}
|
||||||
|
@ -589,28 +769,37 @@ impl InferenceContext<'_> {
|
||||||
// Field type might have some unknown types
|
// Field type might have some unknown types
|
||||||
// FIXME: we may want to emit a single type variable for all instance of type fields?
|
// FIXME: we may want to emit a single type variable for all instance of type fields?
|
||||||
let field_ty = self.insert_type_vars(field_ty);
|
let field_ty = self.insert_type_vars(field_ty);
|
||||||
self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
|
self.infer_expr_coerce(
|
||||||
|
field.expr,
|
||||||
|
&Expectation::has_type(field_ty),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
for field in fields.iter() {
|
for field in fields.iter() {
|
||||||
self.infer_expr_coerce(field.expr, &Expectation::None);
|
// Field projections don't constitute reads.
|
||||||
|
self.infer_expr_coerce(field.expr, &Expectation::None, ExprIsRead::No);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(expr) = spread {
|
if let Some(expr) = spread {
|
||||||
self.infer_expr(*expr, &Expectation::has_type(ty.clone()));
|
self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes);
|
||||||
}
|
}
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
|
Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
|
||||||
Expr::Await { expr } => {
|
Expr::Await { expr } => {
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes);
|
||||||
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
||||||
}
|
}
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
let cast_ty = self.make_ty(type_ref);
|
let cast_ty = self.make_ty(type_ref);
|
||||||
let expr_ty = self.infer_expr(*expr, &Expectation::Castable(cast_ty.clone()));
|
let expr_ty = self.infer_expr(
|
||||||
|
*expr,
|
||||||
|
&Expectation::Castable(cast_ty.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
self.deferred_cast_checks.push(CastCheck::new(
|
self.deferred_cast_checks.push(CastCheck::new(
|
||||||
tgt_expr,
|
tgt_expr,
|
||||||
*expr,
|
*expr,
|
||||||
|
@ -638,7 +827,7 @@ impl InferenceContext<'_> {
|
||||||
} else {
|
} else {
|
||||||
Expectation::none()
|
Expectation::none()
|
||||||
};
|
};
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &expectation);
|
let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes);
|
||||||
match rawness {
|
match rawness {
|
||||||
Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
|
Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
|
||||||
Rawness::Ref => {
|
Rawness::Ref => {
|
||||||
|
@ -650,7 +839,7 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
&Expr::Box { expr } => self.infer_expr_box(expr, expected),
|
&Expr::Box { expr } => self.infer_expr_box(expr, expected),
|
||||||
Expr::UnaryOp { expr, op } => {
|
Expr::UnaryOp { expr, op } => {
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes);
|
||||||
let inner_ty = self.resolve_ty_shallow(&inner_ty);
|
let inner_ty = self.resolve_ty_shallow(&inner_ty);
|
||||||
// FIXME: Note down method resolution her
|
// FIXME: Note down method resolution her
|
||||||
match op {
|
match op {
|
||||||
|
@ -720,19 +909,32 @@ impl InferenceContext<'_> {
|
||||||
// cannot happen in destructuring assignments because of how
|
// cannot happen in destructuring assignments because of how
|
||||||
// they are desugared.
|
// they are desugared.
|
||||||
if is_ordinary {
|
if is_ordinary {
|
||||||
let lhs_ty = self.infer_expr(lhs, &Expectation::none());
|
// LHS of assignment doesn't constitute reads.
|
||||||
self.infer_expr_coerce(*rhs, &Expectation::has_type(lhs_ty));
|
let lhs_ty = self.infer_expr(lhs, &Expectation::none(), ExprIsRead::No);
|
||||||
|
self.infer_expr_coerce(
|
||||||
|
*rhs,
|
||||||
|
&Expectation::has_type(lhs_ty),
|
||||||
|
ExprIsRead::No,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let rhs_ty = self.infer_expr(*rhs, &Expectation::none());
|
let rhs_ty = self.infer_expr(*rhs, &Expectation::none(), ExprIsRead::Yes);
|
||||||
self.infer_assignee_expr(lhs, &rhs_ty);
|
self.infer_assignee_expr(lhs, &rhs_ty);
|
||||||
}
|
}
|
||||||
self.result.standard_types.unit.clone()
|
self.result.standard_types.unit.clone()
|
||||||
}
|
}
|
||||||
Some(BinaryOp::LogicOp(_)) => {
|
Some(BinaryOp::LogicOp(_)) => {
|
||||||
let bool_ty = self.result.standard_types.bool_.clone();
|
let bool_ty = self.result.standard_types.bool_.clone();
|
||||||
self.infer_expr_coerce(*lhs, &Expectation::HasType(bool_ty.clone()));
|
self.infer_expr_coerce(
|
||||||
|
*lhs,
|
||||||
|
&Expectation::HasType(bool_ty.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
let lhs_diverges = self.diverges;
|
let lhs_diverges = self.diverges;
|
||||||
self.infer_expr_coerce(*rhs, &Expectation::HasType(bool_ty.clone()));
|
self.infer_expr_coerce(
|
||||||
|
*rhs,
|
||||||
|
&Expectation::HasType(bool_ty.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
// Depending on the LHS' value, the RHS can never execute.
|
// Depending on the LHS' value, the RHS can never execute.
|
||||||
self.diverges = lhs_diverges;
|
self.diverges = lhs_diverges;
|
||||||
bool_ty
|
bool_ty
|
||||||
|
@ -741,11 +943,12 @@ impl InferenceContext<'_> {
|
||||||
_ => self.err_ty(),
|
_ => self.err_ty(),
|
||||||
},
|
},
|
||||||
Expr::Range { lhs, rhs, range_type } => {
|
Expr::Range { lhs, rhs, range_type } => {
|
||||||
let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none()));
|
let lhs_ty =
|
||||||
|
lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes));
|
||||||
let rhs_expect = lhs_ty
|
let rhs_expect = lhs_ty
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone()));
|
.map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone()));
|
||||||
let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect));
|
let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect, ExprIsRead::Yes));
|
||||||
match (range_type, lhs_ty, rhs_ty) {
|
match (range_type, lhs_ty, rhs_ty) {
|
||||||
(RangeOp::Exclusive, None, None) => match self.resolve_range_full() {
|
(RangeOp::Exclusive, None, None) => match self.resolve_range_full() {
|
||||||
Some(adt) => TyBuilder::adt(self.db, adt).build(),
|
Some(adt) => TyBuilder::adt(self.db, adt).build(),
|
||||||
|
@ -779,8 +982,8 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Index { base, index, is_assignee_expr } => {
|
Expr::Index { base, index, is_assignee_expr } => {
|
||||||
let base_ty = self.infer_expr_inner(*base, &Expectation::none());
|
let base_ty = self.infer_expr_inner(*base, &Expectation::none(), ExprIsRead::Yes);
|
||||||
let index_ty = self.infer_expr(*index, &Expectation::none());
|
let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
|
||||||
|
|
||||||
if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) {
|
if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) {
|
||||||
let canonicalized = self.canonicalize(base_ty.clone());
|
let canonicalized = self.canonicalize(base_ty.clone());
|
||||||
|
@ -851,7 +1054,11 @@ impl InferenceContext<'_> {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (expr, ty) in exprs.iter().zip(tys.iter_mut()) {
|
for (expr, ty) in exprs.iter().zip(tys.iter_mut()) {
|
||||||
*ty = self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone()));
|
*ty = self.infer_expr_coerce(
|
||||||
|
*expr,
|
||||||
|
&Expectation::has_type(ty.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner)
|
TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner)
|
||||||
|
@ -958,7 +1165,7 @@ impl InferenceContext<'_> {
|
||||||
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
|
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
|
||||||
Expr::InlineAsm(asm) => {
|
Expr::InlineAsm(asm) => {
|
||||||
let mut check_expr_asm_operand = |expr, is_input: bool| {
|
let mut check_expr_asm_operand = |expr, is_input: bool| {
|
||||||
let ty = self.infer_expr_no_expect(expr);
|
let ty = self.infer_expr_no_expect(expr, ExprIsRead::Yes);
|
||||||
|
|
||||||
// If this is an input value, we require its type to be fully resolved
|
// If this is an input value, we require its type to be fully resolved
|
||||||
// at this point. This allows us to provide helpful coercions which help
|
// at this point. This allows us to provide helpful coercions which help
|
||||||
|
@ -975,11 +1182,11 @@ impl InferenceContext<'_> {
|
||||||
CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
|
CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
|
||||||
)
|
)
|
||||||
.intern(Interner);
|
.intern(Interner);
|
||||||
_ = self.coerce(Some(expr), &ty, &fnptr_ty);
|
_ = self.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes);
|
||||||
}
|
}
|
||||||
TyKind::Ref(mutbl, _, base_ty) => {
|
TyKind::Ref(mutbl, _, base_ty) => {
|
||||||
let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
|
let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
|
||||||
_ = self.coerce(Some(expr), &ty, &ptr_ty);
|
_ = self.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -1016,7 +1223,9 @@ impl InferenceContext<'_> {
|
||||||
// use a new type variable if we got unknown here
|
// use a new type variable if we got unknown here
|
||||||
let ty = self.insert_type_vars_shallow(ty);
|
let ty = self.insert_type_vars_shallow(ty);
|
||||||
self.write_expr_ty(tgt_expr, ty.clone());
|
self.write_expr_ty(tgt_expr, ty.clone());
|
||||||
if self.resolve_ty_shallow(&ty).is_never() {
|
if self.resolve_ty_shallow(&ty).is_never()
|
||||||
|
&& self.expr_guaranteed_to_constitute_read_for_never(tgt_expr, is_read)
|
||||||
|
{
|
||||||
// Any expression that produces a value of type `!` must have diverged
|
// Any expression that produces a value of type `!` must have diverged
|
||||||
self.diverges = Diverges::Always;
|
self.diverges = Diverges::Always;
|
||||||
}
|
}
|
||||||
|
@ -1041,7 +1250,7 @@ impl InferenceContext<'_> {
|
||||||
let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
|
let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
|
||||||
let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected);
|
let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected);
|
||||||
if let Some(target) = expected.only_has_type(&mut this.table) {
|
if let Some(target) = expected.only_has_type(&mut this.table) {
|
||||||
match this.coerce(Some(tgt_expr), &ty, &target) {
|
match this.coerce(Some(tgt_expr), &ty, &target, CoerceNever::Yes) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
this.result.type_mismatches.insert(
|
this.result.type_mismatches.insert(
|
||||||
|
@ -1153,7 +1362,7 @@ impl InferenceContext<'_> {
|
||||||
Array::ElementList { elements, .. } => {
|
Array::ElementList { elements, .. } => {
|
||||||
let mut coerce = CoerceMany::new(elem_ty);
|
let mut coerce = CoerceMany::new(elem_ty);
|
||||||
for &expr in elements.iter() {
|
for &expr in elements.iter() {
|
||||||
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
|
let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes);
|
||||||
coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr));
|
coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr));
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
|
@ -1162,13 +1371,17 @@ impl InferenceContext<'_> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
&Array::Repeat { initializer, repeat } => {
|
&Array::Repeat { initializer, repeat } => {
|
||||||
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone()));
|
self.infer_expr_coerce(
|
||||||
|
initializer,
|
||||||
|
&Expectation::has_type(elem_ty.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner);
|
let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner);
|
||||||
match self.body[repeat] {
|
match self.body[repeat] {
|
||||||
Expr::Underscore => {
|
Expr::Underscore => {
|
||||||
self.write_expr_ty(repeat, usize);
|
self.write_expr_ty(repeat, usize);
|
||||||
}
|
}
|
||||||
_ => _ = self.infer_expr(repeat, &Expectation::HasType(usize)),
|
_ => _ = self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes),
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
|
@ -1193,7 +1406,8 @@ impl InferenceContext<'_> {
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("infer_return called outside function body")
|
.expect("infer_return called outside function body")
|
||||||
.expected_ty();
|
.expected_ty();
|
||||||
let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty));
|
let return_expr_ty =
|
||||||
|
self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes);
|
||||||
let mut coerce_many = self.return_coercion.take().unwrap();
|
let mut coerce_many = self.return_coercion.take().unwrap();
|
||||||
coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr));
|
coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr));
|
||||||
self.return_coercion = Some(coerce_many);
|
self.return_coercion = Some(coerce_many);
|
||||||
|
@ -1213,7 +1427,7 @@ impl InferenceContext<'_> {
|
||||||
None => {
|
None => {
|
||||||
// FIXME: diagnose return outside of function
|
// FIXME: diagnose return outside of function
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
self.infer_expr_no_expect(expr);
|
self.infer_expr_no_expect(expr, ExprIsRead::Yes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1225,8 +1439,11 @@ impl InferenceContext<'_> {
|
||||||
Some(return_coercion) => {
|
Some(return_coercion) => {
|
||||||
let ret_ty = return_coercion.expected_ty();
|
let ret_ty = return_coercion.expected_ty();
|
||||||
|
|
||||||
let call_expr_ty =
|
let call_expr_ty = self.infer_expr_inner(
|
||||||
self.infer_expr_inner(expr, &Expectation::HasType(ret_ty.clone()));
|
expr,
|
||||||
|
&Expectation::HasType(ret_ty.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
);
|
||||||
|
|
||||||
// NB: this should *not* coerce.
|
// NB: this should *not* coerce.
|
||||||
// tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`).
|
// tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`).
|
||||||
|
@ -1234,7 +1451,7 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// FIXME: diagnose `become` outside of functions
|
// FIXME: diagnose `become` outside of functions
|
||||||
self.infer_expr_no_expect(expr);
|
self.infer_expr_no_expect(expr, ExprIsRead::Yes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1255,7 +1472,7 @@ impl InferenceContext<'_> {
|
||||||
})
|
})
|
||||||
.unwrap_or_else(Expectation::none);
|
.unwrap_or_else(Expectation::none);
|
||||||
|
|
||||||
let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp);
|
let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes);
|
||||||
TyBuilder::adt(self.db, box_id)
|
TyBuilder::adt(self.db, box_id)
|
||||||
.push(inner_ty)
|
.push(inner_ty)
|
||||||
.fill_with_defaults(self.db, || self.table.new_type_var())
|
.fill_with_defaults(self.db, || self.table.new_type_var())
|
||||||
|
@ -1333,12 +1550,13 @@ impl InferenceContext<'_> {
|
||||||
Expr::Underscore => rhs_ty.clone(),
|
Expr::Underscore => rhs_ty.clone(),
|
||||||
_ => {
|
_ => {
|
||||||
// `lhs` is a place expression, a unit struct, or an enum variant.
|
// `lhs` is a place expression, a unit struct, or an enum variant.
|
||||||
let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none());
|
// LHS of assignment doesn't constitute reads.
|
||||||
|
let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none(), ExprIsRead::No);
|
||||||
|
|
||||||
// This is the only branch where this function may coerce any type.
|
// This is the only branch where this function may coerce any type.
|
||||||
// We are returning early to avoid the unifiability check below.
|
// We are returning early to avoid the unifiability check below.
|
||||||
let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
|
let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
|
||||||
let ty = match self.coerce(None, &rhs_ty, &lhs_ty) {
|
let ty = match self.coerce(None, &rhs_ty, &lhs_ty, CoerceNever::Yes) {
|
||||||
Ok(ty) => ty,
|
Ok(ty) => ty,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
self.result.type_mismatches.insert(
|
self.result.type_mismatches.insert(
|
||||||
|
@ -1373,7 +1591,12 @@ impl InferenceContext<'_> {
|
||||||
tgt_expr: ExprId,
|
tgt_expr: ExprId,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let lhs_expectation = Expectation::none();
|
let lhs_expectation = Expectation::none();
|
||||||
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
|
let is_read = if matches!(op, BinaryOp::Assignment { .. }) {
|
||||||
|
ExprIsRead::Yes
|
||||||
|
} else {
|
||||||
|
ExprIsRead::No
|
||||||
|
};
|
||||||
|
let lhs_ty = self.infer_expr(lhs, &lhs_expectation, is_read);
|
||||||
let rhs_ty = self.table.new_type_var();
|
let rhs_ty = self.table.new_type_var();
|
||||||
|
|
||||||
let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| {
|
let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| {
|
||||||
|
@ -1396,7 +1619,7 @@ impl InferenceContext<'_> {
|
||||||
self.err_ty()
|
self.err_ty()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty));
|
self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty), ExprIsRead::Yes);
|
||||||
|
|
||||||
return ret_ty;
|
return ret_ty;
|
||||||
}
|
}
|
||||||
|
@ -1415,7 +1638,7 @@ impl InferenceContext<'_> {
|
||||||
let method_ty = self.db.value_ty(func.into()).unwrap().substitute(Interner, &subst);
|
let method_ty = self.db.value_ty(func.into()).unwrap().substitute(Interner, &subst);
|
||||||
self.register_obligations_for_call(&method_ty);
|
self.register_obligations_for_call(&method_ty);
|
||||||
|
|
||||||
self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()));
|
self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()), ExprIsRead::Yes);
|
||||||
|
|
||||||
let ret_ty = match method_ty.callable_sig(self.db) {
|
let ret_ty = match method_ty.callable_sig(self.db) {
|
||||||
Some(sig) => {
|
Some(sig) => {
|
||||||
|
@ -1487,12 +1710,25 @@ impl InferenceContext<'_> {
|
||||||
.unwrap_or_else(|| this.table.new_type_var());
|
.unwrap_or_else(|| this.table.new_type_var());
|
||||||
|
|
||||||
let ty = if let Some(expr) = initializer {
|
let ty = if let Some(expr) = initializer {
|
||||||
|
// If we have a subpattern that performs a read, we want to consider this
|
||||||
|
// to diverge for compatibility to support something like `let x: () = *never_ptr;`.
|
||||||
|
let target_is_read =
|
||||||
|
if this.pat_guaranteed_to_constitute_read_for_never(*pat) {
|
||||||
|
ExprIsRead::Yes
|
||||||
|
} else {
|
||||||
|
ExprIsRead::No
|
||||||
|
};
|
||||||
let ty = if contains_explicit_ref_binding(this.body, *pat) {
|
let ty = if contains_explicit_ref_binding(this.body, *pat) {
|
||||||
this.infer_expr(*expr, &Expectation::has_type(decl_ty.clone()))
|
this.infer_expr(
|
||||||
|
*expr,
|
||||||
|
&Expectation::has_type(decl_ty.clone()),
|
||||||
|
target_is_read,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
this.infer_expr_coerce(
|
this.infer_expr_coerce(
|
||||||
*expr,
|
*expr,
|
||||||
&Expectation::has_type(decl_ty.clone()),
|
&Expectation::has_type(decl_ty.clone()),
|
||||||
|
target_is_read,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if type_ref.is_some() {
|
if type_ref.is_some() {
|
||||||
|
@ -1512,17 +1748,19 @@ impl InferenceContext<'_> {
|
||||||
this.infer_expr_coerce(
|
this.infer_expr_coerce(
|
||||||
*expr,
|
*expr,
|
||||||
&Expectation::HasType(this.result.standard_types.never.clone()),
|
&Expectation::HasType(this.result.standard_types.never.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
);
|
);
|
||||||
this.diverges = previous_diverges;
|
this.diverges = previous_diverges;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Statement::Expr { expr, has_semi } => {
|
&Statement::Expr { expr, has_semi } => {
|
||||||
if has_semi {
|
if has_semi {
|
||||||
this.infer_expr(expr, &Expectation::none());
|
this.infer_expr(expr, &Expectation::none(), ExprIsRead::Yes);
|
||||||
} else {
|
} else {
|
||||||
this.infer_expr_coerce(
|
this.infer_expr_coerce(
|
||||||
expr,
|
expr,
|
||||||
&Expectation::HasType(this.result.standard_types.unit.clone()),
|
&Expectation::HasType(this.result.standard_types.unit.clone()),
|
||||||
|
ExprIsRead::Yes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1532,7 +1770,7 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
// FIXME: This should make use of the breakable CoerceMany
|
// FIXME: This should make use of the breakable CoerceMany
|
||||||
if let Some(expr) = tail {
|
if let Some(expr) = tail {
|
||||||
this.infer_expr_coerce(expr, expected)
|
this.infer_expr_coerce(expr, expected, ExprIsRead::Yes)
|
||||||
} else {
|
} else {
|
||||||
// Citing rustc: if there is no explicit tail expression,
|
// Citing rustc: if there is no explicit tail expression,
|
||||||
// that is typically equivalent to a tail expression
|
// that is typically equivalent to a tail expression
|
||||||
|
@ -1545,8 +1783,20 @@ impl InferenceContext<'_> {
|
||||||
// we don't even make an attempt at coercion
|
// we don't even make an attempt at coercion
|
||||||
this.table.new_maybe_never_var()
|
this.table.new_maybe_never_var()
|
||||||
} else if let Some(t) = expected.only_has_type(&mut this.table) {
|
} else if let Some(t) = expected.only_has_type(&mut this.table) {
|
||||||
|
let coerce_never = if this
|
||||||
|
.expr_guaranteed_to_constitute_read_for_never(expr, ExprIsRead::Yes)
|
||||||
|
{
|
||||||
|
CoerceNever::Yes
|
||||||
|
} else {
|
||||||
|
CoerceNever::No
|
||||||
|
};
|
||||||
if this
|
if this
|
||||||
.coerce(Some(expr), &this.result.standard_types.unit.clone(), &t)
|
.coerce(
|
||||||
|
Some(expr),
|
||||||
|
&this.result.standard_types.unit.clone(),
|
||||||
|
&t,
|
||||||
|
coerce_never,
|
||||||
|
)
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
this.result.type_mismatches.insert(
|
this.result.type_mismatches.insert(
|
||||||
|
@ -1658,7 +1908,8 @@ impl InferenceContext<'_> {
|
||||||
name: &Name,
|
name: &Name,
|
||||||
expected: &Expectation,
|
expected: &Expectation,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
|
// Field projections don't constitute reads.
|
||||||
|
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No);
|
||||||
|
|
||||||
if name.is_missing() {
|
if name.is_missing() {
|
||||||
// Bail out early, don't even try to look up field. Also, we don't issue an unresolved
|
// Bail out early, don't even try to look up field. Also, we don't issue an unresolved
|
||||||
|
@ -1730,7 +1981,7 @@ impl InferenceContext<'_> {
|
||||||
generic_args: Option<&GenericArgs>,
|
generic_args: Option<&GenericArgs>,
|
||||||
expected: &Expectation,
|
expected: &Expectation,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
|
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes);
|
||||||
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
|
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
|
||||||
|
|
||||||
let resolved = method_resolution::lookup_method(
|
let resolved = method_resolution::lookup_method(
|
||||||
|
@ -1917,7 +2168,7 @@ impl InferenceContext<'_> {
|
||||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||||
let expected = Expectation::rvalue_hint(self, expected_ty);
|
let expected = Expectation::rvalue_hint(self, expected_ty);
|
||||||
// infer with the expected type we have...
|
// infer with the expected type we have...
|
||||||
let ty = self.infer_expr_inner(arg, &expected);
|
let ty = self.infer_expr_inner(arg, &expected, ExprIsRead::Yes);
|
||||||
|
|
||||||
// then coerce to either the expected type or just the formal parameter type
|
// then coerce to either the expected type or just the formal parameter type
|
||||||
let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) {
|
let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) {
|
||||||
|
@ -1931,7 +2182,20 @@ impl InferenceContext<'_> {
|
||||||
// The function signature may contain some unknown types, so we need to insert
|
// The function signature may contain some unknown types, so we need to insert
|
||||||
// type vars here to avoid type mismatch false positive.
|
// type vars here to avoid type mismatch false positive.
|
||||||
let coercion_target = self.insert_type_vars(coercion_target);
|
let coercion_target = self.insert_type_vars(coercion_target);
|
||||||
if self.coerce(Some(arg), &ty, &coercion_target).is_err() && !arg_count_mismatch {
|
|
||||||
|
// Any expression that produces a value of type `!` must have diverged,
|
||||||
|
// unless it's a place expression that isn't being read from, in which case
|
||||||
|
// diverging would be unsound since we may never actually read the `!`.
|
||||||
|
// e.g. `let _ = *never_ptr;` with `never_ptr: *const !`.
|
||||||
|
let coerce_never =
|
||||||
|
if self.expr_guaranteed_to_constitute_read_for_never(arg, ExprIsRead::Yes) {
|
||||||
|
CoerceNever::Yes
|
||||||
|
} else {
|
||||||
|
CoerceNever::No
|
||||||
|
};
|
||||||
|
if self.coerce(Some(arg), &ty, &coercion_target, coerce_never).is_err()
|
||||||
|
&& !arg_count_mismatch
|
||||||
|
{
|
||||||
self.result.type_mismatches.insert(
|
self.result.type_mismatches.insert(
|
||||||
arg.into(),
|
arg.into(),
|
||||||
TypeMismatch { expected: coercion_target, actual: ty.clone() },
|
TypeMismatch { expected: coercion_target, actual: ty.clone() },
|
||||||
|
@ -2106,7 +2370,7 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
let _ty = arg.data(Interner).ty.clone();
|
let _ty = arg.data(Interner).ty.clone();
|
||||||
let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
|
let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
|
||||||
self.infer_expr(args[arg_idx as usize], &expected);
|
self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes);
|
||||||
// FIXME: evaluate and unify with the const
|
// FIXME: evaluate and unify with the const
|
||||||
}
|
}
|
||||||
let mut indices = legacy_const_generics_indices.as_ref().clone();
|
let mut indices = legacy_const_generics_indices.as_ref().clone();
|
||||||
|
|
|
@ -12,12 +12,11 @@ use hir_expand::name::Name;
|
||||||
use intern::sym;
|
use intern::sym;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
infer::Expectation, lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, Interner,
|
infer::{expr::ExprIsRead, Expectation, InferenceContext},
|
||||||
OverloadedDeref, TyBuilder, TyKind,
|
lower::lower_to_chalk_mutability,
|
||||||
|
Adjust, Adjustment, AutoBorrow, Interner, OverloadedDeref, TyBuilder, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::InferenceContext;
|
|
||||||
|
|
||||||
impl InferenceContext<'_> {
|
impl InferenceContext<'_> {
|
||||||
pub(crate) fn infer_mut_body(&mut self) {
|
pub(crate) fn infer_mut_body(&mut self) {
|
||||||
self.infer_mut_expr(self.body.body_expr, Mutability::Not);
|
self.infer_mut_expr(self.body.body_expr, Mutability::Not);
|
||||||
|
@ -164,7 +163,11 @@ impl InferenceContext<'_> {
|
||||||
if let Some(ty) = self.result.type_of_expr.get(index) {
|
if let Some(ty) = self.result.type_of_expr.get(index) {
|
||||||
ty.clone()
|
ty.clone()
|
||||||
} else {
|
} else {
|
||||||
self.infer_expr(index, &Expectation::none())
|
self.infer_expr(
|
||||||
|
index,
|
||||||
|
&Expectation::none(),
|
||||||
|
ExprIsRead::Yes,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
|
let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
|
||||||
.push(base_ty)
|
.push(base_ty)
|
||||||
|
|
|
@ -12,7 +12,7 @@ use stdx::TupleExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::{try_const_usize, usize_const},
|
consteval::{try_const_usize, usize_const},
|
||||||
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
|
infer::{expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch},
|
||||||
lower::lower_to_chalk_mutability,
|
lower::lower_to_chalk_mutability,
|
||||||
primitive::UintTy,
|
primitive::UintTy,
|
||||||
static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
|
static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
|
||||||
|
@ -361,7 +361,7 @@ impl InferenceContext<'_> {
|
||||||
None => self.err_ty(),
|
None => self.err_ty(),
|
||||||
},
|
},
|
||||||
Pat::ConstBlock(expr) => {
|
Pat::ConstBlock(expr) => {
|
||||||
self.infer_expr(*expr, &Expectation::has_type(expected.clone()))
|
self.infer_expr(*expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
|
||||||
}
|
}
|
||||||
Pat::Missing => self.err_ty(),
|
Pat::Missing => self.err_ty(),
|
||||||
};
|
};
|
||||||
|
@ -497,7 +497,7 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.infer_expr(expr, &Expectation::has_type(expected.clone()))
|
self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool {
|
fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool {
|
||||||
|
|
|
@ -539,3 +539,249 @@ fn test() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_place_match1() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn not_a_read() -> ! {
|
||||||
|
unsafe {
|
||||||
|
let x: *const ! = 0 as _;
|
||||||
|
let _: ! = *x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
21..100 '{ ... } }': !
|
||||||
|
27..98 'unsafe... }': !
|
||||||
|
48..49 'x': *const !
|
||||||
|
62..63 '0': i32
|
||||||
|
62..68 '0 as _': *const !
|
||||||
|
82..83 '_': !
|
||||||
|
89..91 '*x': !
|
||||||
|
90..91 'x': *const !
|
||||||
|
27..98: expected !, got ()
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_place_match2() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn not_a_read_implicit() -> ! {
|
||||||
|
unsafe {
|
||||||
|
let x: *const ! = 0 as _;
|
||||||
|
let _ = *x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
30..106 '{ ... } }': !
|
||||||
|
36..104 'unsafe... }': !
|
||||||
|
57..58 'x': *const !
|
||||||
|
71..72 '0': i32
|
||||||
|
71..77 '0 as _': *const !
|
||||||
|
91..92 '_': !
|
||||||
|
95..97 '*x': !
|
||||||
|
96..97 'x': *const !
|
||||||
|
36..104: expected !, got ()
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_place_match3() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn not_a_read_guide_coercion() -> ! {
|
||||||
|
unsafe {
|
||||||
|
let x: *const ! = 0 as _;
|
||||||
|
let _: () = *x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
36..116 '{ ... } }': !
|
||||||
|
42..114 'unsafe... }': !
|
||||||
|
63..64 'x': *const !
|
||||||
|
77..78 '0': i32
|
||||||
|
77..83 '0 as _': *const !
|
||||||
|
97..98 '_': ()
|
||||||
|
105..107 '*x': !
|
||||||
|
106..107 'x': *const !
|
||||||
|
42..114: expected !, got ()
|
||||||
|
105..107: expected (), got !
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_place_match4() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn empty_match() -> ! {
|
||||||
|
unsafe {
|
||||||
|
let x: *const ! = 0 as _;
|
||||||
|
match *x { _ => {} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
22..108 '{ ... } }': !
|
||||||
|
28..106 'unsafe... }': !
|
||||||
|
49..50 'x': *const !
|
||||||
|
63..64 '0': i32
|
||||||
|
63..69 '0 as _': *const !
|
||||||
|
79..99 'match ...> {} }': ()
|
||||||
|
85..87 '*x': !
|
||||||
|
86..87 'x': *const !
|
||||||
|
90..91 '_': !
|
||||||
|
95..97 '{}': ()
|
||||||
|
28..106: expected !, got ()
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_place_match5() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn field_projection() -> ! {
|
||||||
|
unsafe {
|
||||||
|
let x: *const (!, ()) = 0 as _;
|
||||||
|
let _ = (*x).0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
27..113 '{ ... } }': !
|
||||||
|
33..111 'unsafe... }': !
|
||||||
|
54..55 'x': *const (!, ())
|
||||||
|
74..75 '0': i32
|
||||||
|
74..80 '0 as _': *const (!, ())
|
||||||
|
94..95 '_': !
|
||||||
|
98..104 '(*x).0': !
|
||||||
|
99..101 '*x': (!, ())
|
||||||
|
100..101 'x': *const (!, ())
|
||||||
|
33..111: expected !, got ()
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_place_match6() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn covered_arm() -> ! {
|
||||||
|
unsafe {
|
||||||
|
let x: *const ! = 0 as _;
|
||||||
|
let (_ | 1i32) = *x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
22..107 '{ ... } }': !
|
||||||
|
28..105 'unsafe... }': !
|
||||||
|
49..50 'x': *const !
|
||||||
|
63..64 '0': i32
|
||||||
|
63..69 '0 as _': *const !
|
||||||
|
84..85 '_': !
|
||||||
|
84..92 '_ | 1i32': !
|
||||||
|
88..92 '1i32': i32
|
||||||
|
88..92 '1i32': i32
|
||||||
|
96..98 '*x': !
|
||||||
|
97..98 'x': *const !
|
||||||
|
28..105: expected !, got ()
|
||||||
|
88..92: expected !, got i32
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_place_match7() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn uncovered_arm() -> ! {
|
||||||
|
unsafe {
|
||||||
|
let x: *const ! = 0 as _;
|
||||||
|
let (1i32 | _) = *x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
24..109 '{ ... } }': !
|
||||||
|
30..107 'unsafe... }': !
|
||||||
|
51..52 'x': *const !
|
||||||
|
65..66 '0': i32
|
||||||
|
65..71 '0 as _': *const !
|
||||||
|
86..90 '1i32': i32
|
||||||
|
86..90 '1i32': i32
|
||||||
|
86..94 '1i32 | _': !
|
||||||
|
93..94 '_': !
|
||||||
|
98..100 '*x': !
|
||||||
|
99..100 'x': *const !
|
||||||
|
30..107: expected !, got ()
|
||||||
|
86..90: expected !, got i32
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_place_match8() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn coerce_ref_binding() -> ! {
|
||||||
|
unsafe {
|
||||||
|
let x: *const ! = 0 as _;
|
||||||
|
let ref _x: () = *x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
29..114 '{ ... } }': !
|
||||||
|
35..112 'unsafe... }': !
|
||||||
|
56..57 'x': *const !
|
||||||
|
70..71 '0': i32
|
||||||
|
70..76 '0 as _': *const !
|
||||||
|
90..96 'ref _x': &'? ()
|
||||||
|
103..105 '*x': !
|
||||||
|
104..105 'x': *const !
|
||||||
|
103..105: expected (), got !
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn never_place_isnt_diverging() {
|
||||||
|
check_infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- minicore: sized
|
||||||
|
fn make_up_a_pointer<T>() -> *const T {
|
||||||
|
unsafe {
|
||||||
|
let x: *const ! = 0 as _;
|
||||||
|
&raw const *x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
38..116 '{ ... } }': *const T
|
||||||
|
44..114 'unsafe... }': *const T
|
||||||
|
65..66 'x': *const !
|
||||||
|
79..80 '0': i32
|
||||||
|
79..85 '0 as _': *const !
|
||||||
|
95..108 '&raw const *x': *const !
|
||||||
|
106..108 '*x': !
|
||||||
|
107..108 'x': *const !
|
||||||
|
95..108: expected *const T, got *const !
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue