Better handle never type and branch merging
Split out tests for never type to another file
This commit is contained in:
parent
4bb66df6de
commit
bf161fa3e5
|
@ -13,4 +13,5 @@ test_utils::marks!(
|
||||||
infer_while_let
|
infer_while_let
|
||||||
macro_rules_from_other_crates_are_visible_with_macro_use
|
macro_rules_from_other_crates_are_visible_with_macro_use
|
||||||
prelude_is_macro_use
|
prelude_is_macro_use
|
||||||
|
coerce_merge_fail_fallback
|
||||||
);
|
);
|
||||||
|
|
|
@ -297,23 +297,35 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||||
match (ty1, ty2) {
|
match (ty1, ty2) {
|
||||||
(Ty::Unknown, _) | (_, Ty::Unknown) => true,
|
(Ty::Unknown, _) | (_, Ty::Unknown) => true,
|
||||||
|
|
||||||
(Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
|
(Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
|
||||||
| (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
|
| (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
|
||||||
| (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) => {
|
| (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2)))
|
||||||
|
| (
|
||||||
|
Ty::Infer(InferTy::MaybeNeverTypeVar(tv1)),
|
||||||
|
Ty::Infer(InferTy::MaybeNeverTypeVar(tv2)),
|
||||||
|
) => {
|
||||||
// both type vars are unknown since we tried to resolve them
|
// both type vars are unknown since we tried to resolve them
|
||||||
self.var_unification_table.union(*tv1, *tv2);
|
self.var_unification_table.union(*tv1, *tv2);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The order of MaybeNeverTypeVar matters here.
|
||||||
|
// Unifying MaybeNeverTypeVar and TypeVar will let the latter become MaybeNeverTypeVar.
|
||||||
|
// Unifying MaybeNeverTypeVar and other concrete type will let the former become it.
|
||||||
(Ty::Infer(InferTy::TypeVar(tv)), other)
|
(Ty::Infer(InferTy::TypeVar(tv)), other)
|
||||||
| (other, Ty::Infer(InferTy::TypeVar(tv)))
|
| (other, Ty::Infer(InferTy::TypeVar(tv)))
|
||||||
| (Ty::Infer(InferTy::IntVar(tv)), other)
|
| (Ty::Infer(InferTy::MaybeNeverTypeVar(tv)), other)
|
||||||
| (other, Ty::Infer(InferTy::IntVar(tv)))
|
| (other, Ty::Infer(InferTy::MaybeNeverTypeVar(tv)))
|
||||||
| (Ty::Infer(InferTy::FloatVar(tv)), other)
|
| (Ty::Infer(InferTy::IntVar(tv)), other @ ty_app!(TypeCtor::Int(_)))
|
||||||
| (other, Ty::Infer(InferTy::FloatVar(tv))) => {
|
| (other @ ty_app!(TypeCtor::Int(_)), Ty::Infer(InferTy::IntVar(tv)))
|
||||||
|
| (Ty::Infer(InferTy::FloatVar(tv)), other @ ty_app!(TypeCtor::Float(_)))
|
||||||
|
| (other @ ty_app!(TypeCtor::Float(_)), Ty::Infer(InferTy::FloatVar(tv))) => {
|
||||||
// the type var is unknown since we tried to resolve it
|
// the type var is unknown since we tried to resolve it
|
||||||
self.var_unification_table.union_value(*tv, TypeVarValue::Known(other.clone()));
|
self.var_unification_table.union_value(*tv, TypeVarValue::Known(other.clone()));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -330,6 +342,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
Ty::Infer(InferTy::FloatVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
|
Ty::Infer(InferTy::FloatVar(self.var_unification_table.new_key(TypeVarValue::Unknown)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_maybe_never_type_var(&mut self) -> Ty {
|
||||||
|
Ty::Infer(InferTy::MaybeNeverTypeVar(
|
||||||
|
self.var_unification_table.new_key(TypeVarValue::Unknown),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
|
/// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
|
||||||
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
|
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
|
||||||
match ty {
|
match ty {
|
||||||
|
@ -817,6 +835,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Merge two types from different branches, with possible implicit coerce.
|
||||||
|
///
|
||||||
|
/// Note that it is only possible that one type are coerced to another.
|
||||||
|
/// Coercing both types to another least upper bound type is not possible in rustc,
|
||||||
|
/// which will simply result in "incompatible types" error.
|
||||||
|
fn coerce_merge_branch<'t>(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
|
||||||
|
if self.coerce(ty1, ty2) {
|
||||||
|
ty2.clone()
|
||||||
|
} else if self.coerce(ty2, ty1) {
|
||||||
|
ty1.clone()
|
||||||
|
} else {
|
||||||
|
tested_by!(coerce_merge_fail_fallback);
|
||||||
|
// For incompatible types, we use the latter one as result
|
||||||
|
// to be better recovery for `if` without `else`.
|
||||||
|
ty2.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
|
@ -828,12 +864,26 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
|
fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
|
||||||
match (&mut from_ty, &*to_ty) {
|
match (&from_ty, to_ty) {
|
||||||
// Top and bottom type
|
// Never type will make type variable to fallback to Never Type instead of Unknown.
|
||||||
|
(ty_app!(TypeCtor::Never), Ty::Infer(InferTy::TypeVar(tv))) => {
|
||||||
|
let var = self.new_maybe_never_type_var();
|
||||||
|
self.var_unification_table.union_value(*tv, TypeVarValue::Known(var));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
(ty_app!(TypeCtor::Never), _) => return true,
|
(ty_app!(TypeCtor::Never), _) => return true,
|
||||||
|
|
||||||
// FIXME: Solve `FromTy: CoerceUnsized<ToTy>` instead of listing common impls here.
|
// Trivial cases, this should go after `never` check to
|
||||||
|
// avoid infer result type to be never
|
||||||
|
_ => {
|
||||||
|
if self.unify_inner_trivial(&from_ty, &to_ty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pointer weakening and function to pointer
|
||||||
|
match (&mut from_ty, to_ty) {
|
||||||
// `*mut T`, `&mut T, `&T`` -> `*const T`
|
// `*mut T`, `&mut T, `&T`` -> `*const T`
|
||||||
// `&mut T` -> `&T`
|
// `&mut T` -> `&T`
|
||||||
// `&mut T` -> `*mut T`
|
// `&mut T` -> `*mut T`
|
||||||
|
@ -866,71 +916,67 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trivial cases, this should go after `never` check to
|
_ => {}
|
||||||
// avoid infer result type to be never
|
|
||||||
_ => {
|
|
||||||
if self.unify_inner_trivial(&from_ty, &to_ty) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try coerce or unify
|
// FIXME: Solve `FromTy: CoerceUnsized<ToTy>` instead of listing common impls here.
|
||||||
match (&from_ty, &to_ty) {
|
match (&from_ty, &to_ty) {
|
||||||
// FIXME: Solve `FromTy: CoerceUnsized<ToTy>` instead of listing common impls here.
|
// Mutilibity is checked above
|
||||||
(ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2))
|
(ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2))
|
||||||
| (ty_app!(TypeCtor::RawPtr(_), st1), ty_app!(TypeCtor::RawPtr(_), st2)) => {
|
| (ty_app!(TypeCtor::RawPtr(_), st1), ty_app!(TypeCtor::RawPtr(_), st2)) => {
|
||||||
match self.try_coerce_unsized(&st1[0], &st2[0], 0) {
|
if self.try_coerce_unsized(&st1[0], &st2[0], 0) {
|
||||||
Some(ret) => return ret,
|
return true;
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto Deref if cannot coerce
|
// Auto Deref if cannot coerce
|
||||||
match (&from_ty, &to_ty) {
|
match (&from_ty, to_ty) {
|
||||||
|
// FIXME: DerefMut
|
||||||
(ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) => {
|
(ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) => {
|
||||||
self.unify_autoderef_behind_ref(&st1[0], &st2[0])
|
self.unify_autoderef_behind_ref(&st1[0], &st2[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal unify
|
// Otherwise, normal unify
|
||||||
_ => self.unify(&from_ty, &to_ty),
|
_ => self.unify(&from_ty, to_ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce a type to a DST if `FromTy: Unsize<ToTy>`
|
/// Coerce a type to a DST if `FromTy: Unsize<ToTy>`
|
||||||
///
|
///
|
||||||
/// See: `https://doc.rust-lang.org/nightly/std/marker/trait.Unsize.html`
|
/// See: `https://doc.rust-lang.org/nightly/std/marker/trait.Unsize.html`
|
||||||
fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> Option<bool> {
|
fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty, depth: usize) -> bool {
|
||||||
if depth > 1000 {
|
if depth > 1000 {
|
||||||
panic!("Infinite recursion in coercion");
|
panic!("Infinite recursion in coercion");
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Correctly handle
|
|
||||||
match (&from_ty, &to_ty) {
|
match (&from_ty, &to_ty) {
|
||||||
// `[T; N]` -> `[T]`
|
// `[T; N]` -> `[T]`
|
||||||
(ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => {
|
(ty_app!(TypeCtor::Array, st1), ty_app!(TypeCtor::Slice, st2)) => {
|
||||||
Some(self.unify(&st1[0], &st2[0]))
|
self.unify(&st1[0], &st2[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// `T` -> `dyn Trait` when `T: Trait`
|
// `T` -> `dyn Trait` when `T: Trait`
|
||||||
(_, Ty::Dyn(_)) => {
|
(_, Ty::Dyn(_)) => {
|
||||||
// FIXME: Check predicates
|
// FIXME: Check predicates
|
||||||
Some(true)
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty_app!(ctor1, st1), ty_app!(ctor2, st2)) if ctor1 == ctor2 => {
|
(
|
||||||
|
ty_app!(TypeCtor::Adt(Adt::Struct(struct1)), st1),
|
||||||
|
ty_app!(TypeCtor::Adt(Adt::Struct(struct2)), st2),
|
||||||
|
) if struct1 == struct2 => {
|
||||||
|
// FIXME: Check preconditions here
|
||||||
for (ty1, ty2) in st1.iter().zip(st2.iter()) {
|
for (ty1, ty2) in st1.iter().zip(st2.iter()) {
|
||||||
match self.try_coerce_unsized(ty1, ty2, depth + 1) {
|
if !self.try_coerce_unsized(ty1, ty2, depth + 1) {
|
||||||
Some(true) => {}
|
return false;
|
||||||
ret => return ret,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(true)
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => None,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -980,18 +1026,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
|
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
|
||||||
|
|
||||||
let then_ty = self.infer_expr_inner(*then_branch, &expected);
|
let then_ty = self.infer_expr_inner(*then_branch, &expected);
|
||||||
self.coerce(&then_ty, &expected.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 => Ty::unit(),
|
None => Ty::unit(),
|
||||||
};
|
};
|
||||||
if !self.coerce(&else_ty, &expected.ty) {
|
|
||||||
self.coerce(&expected.ty, &else_ty);
|
self.coerce_merge_branch(&then_ty, &else_ty)
|
||||||
else_ty.clone()
|
|
||||||
} else {
|
|
||||||
expected.ty.clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected),
|
Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected),
|
||||||
Expr::TryBlock { body } => {
|
Expr::TryBlock { body } => {
|
||||||
|
@ -1087,11 +1127,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
.infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()),
|
.infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()),
|
||||||
Expr::Match { expr, arms } => {
|
Expr::Match { expr, arms } => {
|
||||||
let input_ty = self.infer_expr(*expr, &Expectation::none());
|
let input_ty = self.infer_expr(*expr, &Expectation::none());
|
||||||
let mut expected = match expected.ty {
|
|
||||||
Ty::Unknown => Expectation::has_type(Ty::simple(TypeCtor::Never)),
|
let mut result_ty = self.new_maybe_never_type_var();
|
||||||
_ => expected.clone(),
|
|
||||||
};
|
|
||||||
let mut all_never = true;
|
|
||||||
|
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
for &pat in &arm.pats {
|
for &pat in &arm.pats {
|
||||||
|
@ -1103,22 +1140,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
&Expectation::has_type(Ty::simple(TypeCtor::Bool)),
|
&Expectation::has_type(Ty::simple(TypeCtor::Bool)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
||||||
match &arm_ty {
|
result_ty = self.coerce_merge_branch(&result_ty, &arm_ty);
|
||||||
ty_app!(TypeCtor::Never) => (),
|
|
||||||
_ => all_never = false,
|
|
||||||
}
|
|
||||||
if !self.coerce(&arm_ty, &expected.ty) {
|
|
||||||
self.coerce(&expected.ty, &arm_ty);
|
|
||||||
expected = Expectation::has_type(arm_ty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if all_never {
|
result_ty
|
||||||
Ty::simple(TypeCtor::Never)
|
|
||||||
} else {
|
|
||||||
expected.ty
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
// FIXME this could be more efficient...
|
// FIXME this could be more efficient...
|
||||||
|
@ -1558,12 +1585,16 @@ pub enum InferTy {
|
||||||
TypeVar(TypeVarId),
|
TypeVar(TypeVarId),
|
||||||
IntVar(TypeVarId),
|
IntVar(TypeVarId),
|
||||||
FloatVar(TypeVarId),
|
FloatVar(TypeVarId),
|
||||||
|
MaybeNeverTypeVar(TypeVarId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InferTy {
|
impl InferTy {
|
||||||
fn to_inner(self) -> TypeVarId {
|
fn to_inner(self) -> TypeVarId {
|
||||||
match self {
|
match self {
|
||||||
InferTy::TypeVar(ty) | InferTy::IntVar(ty) | InferTy::FloatVar(ty) => ty,
|
InferTy::TypeVar(ty)
|
||||||
|
| InferTy::IntVar(ty)
|
||||||
|
| InferTy::FloatVar(ty)
|
||||||
|
| InferTy::MaybeNeverTypeVar(ty) => ty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1576,6 +1607,7 @@ impl InferTy {
|
||||||
InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(
|
InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(
|
||||||
primitive::UncertainFloatTy::Known(primitive::FloatTy::f64()),
|
primitive::UncertainFloatTy::Known(primitive::FloatTy::f64()),
|
||||||
)),
|
)),
|
||||||
|
InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ where
|
||||||
InferTy::TypeVar(_) => InferTy::TypeVar(root),
|
InferTy::TypeVar(_) => InferTy::TypeVar(root),
|
||||||
InferTy::IntVar(_) => InferTy::IntVar(root),
|
InferTy::IntVar(_) => InferTy::IntVar(root),
|
||||||
InferTy::FloatVar(_) => InferTy::FloatVar(root),
|
InferTy::FloatVar(_) => InferTy::FloatVar(root),
|
||||||
|
InferTy::MaybeNeverTypeVar(_) => InferTy::MaybeNeverTypeVar(root),
|
||||||
};
|
};
|
||||||
let position = self.add(free_var);
|
let position = self.add(free_var);
|
||||||
Ty::Bound(position as u32)
|
Ty::Bound(position as u32)
|
||||||
|
|
|
@ -20,6 +20,8 @@ use crate::{
|
||||||
// against snapshots of the expected results using insta. Use cargo-insta to
|
// against snapshots of the expected results using insta. Use cargo-insta to
|
||||||
// update the snapshots.
|
// update the snapshots.
|
||||||
|
|
||||||
|
mod never_type;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_await() {
|
fn infer_await() {
|
||||||
let (mut db, pos) = MockDatabase::with_position(
|
let (mut db, pos) = MockDatabase::with_position(
|
||||||
|
@ -1077,6 +1079,42 @@ fn test(i: i32) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn coerce_merge_one_by_one1() {
|
||||||
|
covers!(coerce_merge_fail_fallback);
|
||||||
|
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
fn test() {
|
||||||
|
let t = &mut 1;
|
||||||
|
let x = match 1 {
|
||||||
|
1 => t as *mut i32,
|
||||||
|
2 => t as &i32,
|
||||||
|
_ => t as *const i32,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[11; 145) '{ ... }; }': ()
|
||||||
|
[21; 22) 't': &mut i32
|
||||||
|
[25; 31) '&mut 1': &mut i32
|
||||||
|
[30; 31) '1': i32
|
||||||
|
[41; 42) 'x': *const i32
|
||||||
|
[45; 142) 'match ... }': *const i32
|
||||||
|
[51; 52) '1': i32
|
||||||
|
[63; 64) '1': i32
|
||||||
|
[68; 69) 't': &mut i32
|
||||||
|
[68; 81) 't as *mut i32': *mut i32
|
||||||
|
[91; 92) '2': i32
|
||||||
|
[96; 97) 't': &mut i32
|
||||||
|
[96; 105) 't as &i32': &i32
|
||||||
|
[115; 116) '_': i32
|
||||||
|
[120; 121) 't': &mut i32
|
||||||
|
[120; 135) 't as *const i32': *const i32
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bug_484() {
|
fn bug_484() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
@ -2458,7 +2496,6 @@ fn extra_compiler_flags() {
|
||||||
}
|
}
|
||||||
"#),
|
"#),
|
||||||
@r###"
|
@r###"
|
||||||
|
|
||||||
[27; 323) '{ ... } }': ()
|
[27; 323) '{ ... } }': ()
|
||||||
[33; 321) 'for co... }': ()
|
[33; 321) 'for co... }': ()
|
||||||
[37; 44) 'content': &{unknown}
|
[37; 44) 'content': &{unknown}
|
||||||
|
@ -2472,8 +2509,8 @@ fn extra_compiler_flags() {
|
||||||
[135; 167) '{ ... }': &&{unknown}
|
[135; 167) '{ ... }': &&{unknown}
|
||||||
[149; 157) '&content': &&{unknown}
|
[149; 157) '&content': &&{unknown}
|
||||||
[150; 157) 'content': &{unknown}
|
[150; 157) 'content': &{unknown}
|
||||||
[182; 189) 'content': &&{unknown}
|
[182; 189) 'content': &{unknown}
|
||||||
[192; 314) 'if ICE... }': &&{unknown}
|
[192; 314) 'if ICE... }': &{unknown}
|
||||||
[195; 232) 'ICE_RE..._VALUE': {unknown}
|
[195; 232) 'ICE_RE..._VALUE': {unknown}
|
||||||
[195; 248) 'ICE_RE...&name)': bool
|
[195; 248) 'ICE_RE...&name)': bool
|
||||||
[242; 247) '&name': &&&{unknown}
|
[242; 247) '&name': &&&{unknown}
|
||||||
|
@ -4683,121 +4720,3 @@ fn no_such_field_diagnostics() {
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod branching_with_never_tests {
|
|
||||||
use super::type_at;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_never() {
|
|
||||||
let t = type_at(
|
|
||||||
r#"
|
|
||||||
//- /main.rs
|
|
||||||
fn test() {
|
|
||||||
let i = if true {
|
|
||||||
loop {}
|
|
||||||
} else {
|
|
||||||
3.0
|
|
||||||
};
|
|
||||||
i<|>
|
|
||||||
()
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
assert_eq!(t, "f64");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_else_never() {
|
|
||||||
let t = type_at(
|
|
||||||
r#"
|
|
||||||
//- /main.rs
|
|
||||||
fn test(input: bool) {
|
|
||||||
let i = if input {
|
|
||||||
2.0
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
i<|>
|
|
||||||
()
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
assert_eq!(t, "f64");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn match_first_arm_never() {
|
|
||||||
let t = type_at(
|
|
||||||
r#"
|
|
||||||
//- /main.rs
|
|
||||||
fn test(a: i32) {
|
|
||||||
let i = match a {
|
|
||||||
1 => return,
|
|
||||||
2 => 2.0,
|
|
||||||
3 => loop {},
|
|
||||||
_ => 3.0,
|
|
||||||
};
|
|
||||||
i<|>
|
|
||||||
()
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
assert_eq!(t, "f64");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn match_second_arm_never() {
|
|
||||||
let t = type_at(
|
|
||||||
r#"
|
|
||||||
//- /main.rs
|
|
||||||
fn test(a: i32) {
|
|
||||||
let i = match a {
|
|
||||||
1 => 3.0,
|
|
||||||
2 => loop {},
|
|
||||||
3 => 3.0,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
i<|>
|
|
||||||
()
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
assert_eq!(t, "f64");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn match_all_arms_never() {
|
|
||||||
let t = type_at(
|
|
||||||
r#"
|
|
||||||
//- /main.rs
|
|
||||||
fn test(a: i32) {
|
|
||||||
let i = match a {
|
|
||||||
2 => return,
|
|
||||||
_ => loop {},
|
|
||||||
};
|
|
||||||
i<|>
|
|
||||||
()
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
assert_eq!(t, "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn match_no_never_arms() {
|
|
||||||
let t = type_at(
|
|
||||||
r#"
|
|
||||||
//- /main.rs
|
|
||||||
fn test(a: i32) {
|
|
||||||
let i = match a {
|
|
||||||
2 => 2.0,
|
|
||||||
_ => 3.0,
|
|
||||||
};
|
|
||||||
i<|>
|
|
||||||
()
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
assert_eq!(t, "f64");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
258
crates/ra_hir/src/ty/tests/never_type.rs
Normal file
258
crates/ra_hir/src/ty/tests/never_type.rs
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
use super::type_at;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_never1() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test() {
|
||||||
|
let t = return;
|
||||||
|
t<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_never2() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Foo { fn gen() -> Self; }
|
||||||
|
impl Foo for ! { fn gen() -> Self { loop {} } }
|
||||||
|
impl Foo for () { fn gen() -> Self { loop {} } }
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let a = Foo::gen();
|
||||||
|
if false { a } else { loop {} };
|
||||||
|
a<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_never3() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Foo { fn gen() -> Self; }
|
||||||
|
impl Foo for ! { fn gen() -> Self { loop {} } }
|
||||||
|
impl Foo for () { fn gen() -> Self { loop {} } }
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let a = Foo::gen();
|
||||||
|
if false { loop {} } else { a };
|
||||||
|
a<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn never_type_in_generic_args() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
enum Option<T> { None, Some(T) }
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let a = if true { Option::None } else { Option::Some(return) };
|
||||||
|
a<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "Option<!>");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn never_type_can_be_reinferred1() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Foo { fn gen() -> Self; }
|
||||||
|
impl Foo for ! { fn gen() -> Self { loop {} } }
|
||||||
|
impl Foo for () { fn gen() -> Self { loop {} } }
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let a = Foo::gen();
|
||||||
|
if false { loop {} } else { a };
|
||||||
|
a<|>;
|
||||||
|
if false { a };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "()");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn never_type_can_be_reinferred2() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
enum Option<T> { None, Some(T) }
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let a = if true { Option::None } else { Option::Some(return) };
|
||||||
|
a<|>;
|
||||||
|
match 42 {
|
||||||
|
42 => a,
|
||||||
|
_ => Option::Some(42),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "Option<i32>");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn never_type_can_be_reinferred3() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
enum Option<T> { None, Some(T) }
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let a = if true { Option::None } else { Option::Some(return) };
|
||||||
|
a<|>;
|
||||||
|
match 42 {
|
||||||
|
42 => a,
|
||||||
|
_ => Option::Some("str"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "Option<&str>");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_no_arm() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
|
fn test(a: Void) {
|
||||||
|
let t = match a {};
|
||||||
|
t<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_never() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test() {
|
||||||
|
let i = if true {
|
||||||
|
loop {}
|
||||||
|
} else {
|
||||||
|
3.0
|
||||||
|
};
|
||||||
|
i<|>
|
||||||
|
()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "f64");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_else_never() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test(input: bool) {
|
||||||
|
let i = if input {
|
||||||
|
2.0
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
};
|
||||||
|
i<|>
|
||||||
|
()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "f64");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_first_arm_never() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test(a: i32) {
|
||||||
|
let i = match a {
|
||||||
|
1 => return,
|
||||||
|
2 => 2.0,
|
||||||
|
3 => loop {},
|
||||||
|
_ => 3.0,
|
||||||
|
};
|
||||||
|
i<|>
|
||||||
|
()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "f64");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_second_arm_never() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test(a: i32) {
|
||||||
|
let i = match a {
|
||||||
|
1 => 3.0,
|
||||||
|
2 => loop {},
|
||||||
|
3 => 3.0,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
i<|>
|
||||||
|
()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "f64");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_all_arms_never() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test(a: i32) {
|
||||||
|
let i = match a {
|
||||||
|
2 => return,
|
||||||
|
_ => loop {},
|
||||||
|
};
|
||||||
|
i<|>
|
||||||
|
()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_no_never_arms() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test(a: i32) {
|
||||||
|
let i = match a {
|
||||||
|
2 => 2.0,
|
||||||
|
_ => 3.0,
|
||||||
|
};
|
||||||
|
i<|>
|
||||||
|
()
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "f64");
|
||||||
|
}
|
Loading…
Reference in a new issue