mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
Merge #4377
4377: Implement better handling of divergence r=matklad a=flodiebold Divergence here means that for some reason, the end of a block will not be reached. We tried to model this just using the never type, but that doesn't work fully (e.g. in `let x = { loop {}; "foo" };` x should still have type `&str`); so this introduces a `diverges` flag that the type checker keeps track of, like rustc does. We also add some checking for `break`, but no support for break-with-value or labeled breaks yet. Co-authored-by: Florian Diebold <florian.diebold@freiheit.com> Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
f9ec7cebef
9 changed files with 362 additions and 25 deletions
|
@ -131,3 +131,31 @@ impl AstDiagnostic for MissingOkInTailExpr {
|
||||||
ast::Expr::cast(node).unwrap()
|
ast::Expr::cast(node).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BreakOutsideOfLoop {
|
||||||
|
pub file: HirFileId,
|
||||||
|
pub expr: AstPtr<ast::Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for BreakOutsideOfLoop {
|
||||||
|
fn message(&self) -> String {
|
||||||
|
"break outside of loop".to_string()
|
||||||
|
}
|
||||||
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
|
InFile { file_id: self.file, value: self.expr.clone().into() }
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstDiagnostic for BreakOutsideOfLoop {
|
||||||
|
type AST = ast::Expr;
|
||||||
|
|
||||||
|
fn ast(&self, db: &impl AstDatabase) -> Self::AST {
|
||||||
|
let root = db.parse_or_expand(self.file).unwrap();
|
||||||
|
let node = self.source().value.to_node(&root);
|
||||||
|
ast::Expr::cast(node).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -210,6 +210,13 @@ struct InferenceContext<'a> {
|
||||||
/// closures, but currently this is the only field that will change there,
|
/// closures, but currently this is the only field that will change there,
|
||||||
/// so it doesn't make sense.
|
/// so it doesn't make sense.
|
||||||
return_ty: Ty,
|
return_ty: Ty,
|
||||||
|
diverges: Diverges,
|
||||||
|
breakables: Vec<BreakableContext>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct BreakableContext {
|
||||||
|
pub may_break: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
|
@ -224,6 +231,8 @@ impl<'a> InferenceContext<'a> {
|
||||||
owner,
|
owner,
|
||||||
body: db.body(owner),
|
body: db.body(owner),
|
||||||
resolver,
|
resolver,
|
||||||
|
diverges: Diverges::Maybe,
|
||||||
|
breakables: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,15 +675,57 @@ impl Expectation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
enum Diverges {
|
||||||
|
Maybe,
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diverges {
|
||||||
|
fn is_always(self) -> bool {
|
||||||
|
self == Diverges::Always
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::BitAnd for Diverges {
|
||||||
|
type Output = Self;
|
||||||
|
fn bitand(self, other: Self) -> Self {
|
||||||
|
std::cmp::min(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::BitOr for Diverges {
|
||||||
|
type Output = Self;
|
||||||
|
fn bitor(self, other: Self) -> Self {
|
||||||
|
std::cmp::max(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::BitAndAssign for Diverges {
|
||||||
|
fn bitand_assign(&mut self, other: Self) {
|
||||||
|
*self = *self & other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::BitOrAssign for Diverges {
|
||||||
|
fn bitor_assign(&mut self, other: Self) {
|
||||||
|
*self = *self | other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod diagnostics {
|
mod diagnostics {
|
||||||
use hir_def::{expr::ExprId, FunctionId};
|
use hir_def::{expr::ExprId, FunctionId};
|
||||||
use hir_expand::diagnostics::DiagnosticSink;
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
|
|
||||||
use crate::{db::HirDatabase, diagnostics::NoSuchField};
|
use crate::{
|
||||||
|
db::HirDatabase,
|
||||||
|
diagnostics::{BreakOutsideOfLoop, NoSuchField},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub(super) enum InferenceDiagnostic {
|
pub(super) enum InferenceDiagnostic {
|
||||||
NoSuchField { expr: ExprId, field: usize },
|
NoSuchField { expr: ExprId, field: usize },
|
||||||
|
BreakOutsideOfLoop { expr: ExprId },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InferenceDiagnostic {
|
impl InferenceDiagnostic {
|
||||||
|
@ -690,6 +741,13 @@ mod diagnostics {
|
||||||
let field = source_map.field_syntax(*expr, *field);
|
let field = source_map.field_syntax(*expr, *field);
|
||||||
sink.push(NoSuchField { file: field.file_id, field: field.value })
|
sink.push(NoSuchField { file: field.file_id, field: field.value })
|
||||||
}
|
}
|
||||||
|
InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
|
||||||
|
let (_, source_map) = db.body_with_source_map(owner.into());
|
||||||
|
let ptr = source_map
|
||||||
|
.expr_syntax(*expr)
|
||||||
|
.expect("break outside of loop in synthetic syntax");
|
||||||
|
sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Type inference for expressions.
|
//! Type inference for expressions.
|
||||||
|
|
||||||
use std::iter::{repeat, repeat_with};
|
use std::iter::{repeat, repeat_with};
|
||||||
use std::sync::Arc;
|
use std::{mem, sync::Arc};
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
builtin_type::Signedness,
|
builtin_type::Signedness,
|
||||||
|
@ -21,11 +21,18 @@ use crate::{
|
||||||
Ty, TypeCtor, Uncertain,
|
Ty, TypeCtor, Uncertain,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch};
|
use super::{
|
||||||
|
BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
|
||||||
|
TypeMismatch,
|
||||||
|
};
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||||
let ty = self.infer_expr_inner(tgt_expr, expected);
|
let ty = self.infer_expr_inner(tgt_expr, expected);
|
||||||
|
if ty.is_never() {
|
||||||
|
// Any expression that produces a value of type `!` must have diverged
|
||||||
|
self.diverges = Diverges::Always;
|
||||||
|
}
|
||||||
let could_unify = self.unify(&ty, &expected.ty);
|
let could_unify = self.unify(&ty, &expected.ty);
|
||||||
if !could_unify {
|
if !could_unify {
|
||||||
self.result.type_mismatches.insert(
|
self.result.type_mismatches.insert(
|
||||||
|
@ -64,11 +71,18 @@ impl<'a> InferenceContext<'a> {
|
||||||
// if let is desugared to match, so this is always simple if
|
// if let is desugared to match, so this is always simple if
|
||||||
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
|
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
|
||||||
|
|
||||||
|
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
|
let mut both_arms_diverge = Diverges::Always;
|
||||||
|
|
||||||
let then_ty = self.infer_expr_inner(*then_branch, &expected);
|
let then_ty = self.infer_expr_inner(*then_branch, &expected);
|
||||||
|
both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
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(),
|
||||||
};
|
};
|
||||||
|
both_arms_diverge &= self.diverges;
|
||||||
|
|
||||||
|
self.diverges = condition_diverges | both_arms_diverge;
|
||||||
|
|
||||||
self.coerce_merge_branch(&then_ty, &else_ty)
|
self.coerce_merge_branch(&then_ty, &else_ty)
|
||||||
}
|
}
|
||||||
|
@ -79,24 +93,43 @@ impl<'a> InferenceContext<'a> {
|
||||||
Ty::Unknown
|
Ty::Unknown
|
||||||
}
|
}
|
||||||
Expr::Loop { body } => {
|
Expr::Loop { body } => {
|
||||||
|
self.breakables.push(BreakableContext { may_break: false });
|
||||||
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
||||||
|
|
||||||
|
let ctxt = self.breakables.pop().expect("breakable stack broken");
|
||||||
|
if ctxt.may_break {
|
||||||
|
self.diverges = Diverges::Maybe;
|
||||||
|
}
|
||||||
// FIXME handle break with value
|
// FIXME handle break with value
|
||||||
Ty::simple(TypeCtor::Never)
|
if ctxt.may_break {
|
||||||
|
Ty::unit()
|
||||||
|
} else {
|
||||||
|
Ty::simple(TypeCtor::Never)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr::While { condition, body } => {
|
Expr::While { condition, body } => {
|
||||||
|
self.breakables.push(BreakableContext { may_break: false });
|
||||||
// while let is desugared to a match loop, so this is always simple while
|
// while let is desugared to a match loop, so this is always simple while
|
||||||
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
|
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
|
||||||
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
||||||
|
let _ctxt = self.breakables.pop().expect("breakable stack broken");
|
||||||
|
// the body may not run, so it diverging doesn't mean we diverge
|
||||||
|
self.diverges = Diverges::Maybe;
|
||||||
Ty::unit()
|
Ty::unit()
|
||||||
}
|
}
|
||||||
Expr::For { iterable, body, pat } => {
|
Expr::For { iterable, body, pat } => {
|
||||||
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
|
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
|
||||||
|
|
||||||
|
self.breakables.push(BreakableContext { may_break: false });
|
||||||
let pat_ty =
|
let pat_ty =
|
||||||
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
|
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
|
||||||
|
|
||||||
self.infer_pat(*pat, &pat_ty, BindingMode::default());
|
self.infer_pat(*pat, &pat_ty, BindingMode::default());
|
||||||
|
|
||||||
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
||||||
|
let _ctxt = self.breakables.pop().expect("breakable stack broken");
|
||||||
|
// the body may not run, so it diverging doesn't mean we diverge
|
||||||
|
self.diverges = Diverges::Maybe;
|
||||||
Ty::unit()
|
Ty::unit()
|
||||||
}
|
}
|
||||||
Expr::Lambda { body, args, ret_type, arg_types } => {
|
Expr::Lambda { body, args, ret_type, arg_types } => {
|
||||||
|
@ -132,10 +165,12 @@ impl<'a> InferenceContext<'a> {
|
||||||
// infer the body.
|
// infer the body.
|
||||||
self.coerce(&closure_ty, &expected.ty);
|
self.coerce(&closure_ty, &expected.ty);
|
||||||
|
|
||||||
let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone());
|
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||||
|
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
|
||||||
|
|
||||||
self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
|
self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
|
||||||
|
|
||||||
|
self.diverges = prev_diverges;
|
||||||
self.return_ty = prev_ret_ty;
|
self.return_ty = prev_ret_ty;
|
||||||
|
|
||||||
closure_ty
|
closure_ty
|
||||||
|
@ -165,7 +200,11 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.table.new_type_var()
|
self.table.new_type_var()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let matchee_diverges = self.diverges;
|
||||||
|
let mut all_arms_diverge = Diverges::Always;
|
||||||
|
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
|
self.diverges = Diverges::Maybe;
|
||||||
let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
|
let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
|
||||||
if let Some(guard_expr) = arm.guard {
|
if let Some(guard_expr) = arm.guard {
|
||||||
self.infer_expr(
|
self.infer_expr(
|
||||||
|
@ -175,9 +214,12 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
||||||
|
all_arms_diverge &= self.diverges;
|
||||||
result_ty = self.coerce_merge_branch(&result_ty, &arm_ty);
|
result_ty = self.coerce_merge_branch(&result_ty, &arm_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.diverges = matchee_diverges | all_arms_diverge;
|
||||||
|
|
||||||
result_ty
|
result_ty
|
||||||
}
|
}
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
|
@ -191,6 +233,13 @@ impl<'a> InferenceContext<'a> {
|
||||||
// FIXME handle break with value
|
// FIXME handle break with value
|
||||||
self.infer_expr(*expr, &Expectation::none());
|
self.infer_expr(*expr, &Expectation::none());
|
||||||
}
|
}
|
||||||
|
if let Some(ctxt) = self.breakables.last_mut() {
|
||||||
|
ctxt.may_break = true;
|
||||||
|
} else {
|
||||||
|
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
||||||
|
expr: tgt_expr,
|
||||||
|
});
|
||||||
|
}
|
||||||
Ty::simple(TypeCtor::Never)
|
Ty::simple(TypeCtor::Never)
|
||||||
}
|
}
|
||||||
Expr::Return { expr } => {
|
Expr::Return { expr } => {
|
||||||
|
@ -522,7 +571,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
tail: Option<ExprId>,
|
tail: Option<ExprId>,
|
||||||
expected: &Expectation,
|
expected: &Expectation,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let mut diverges = false;
|
|
||||||
for stmt in statements {
|
for stmt in statements {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Let { pat, type_ref, initializer } => {
|
Statement::Let { pat, type_ref, initializer } => {
|
||||||
|
@ -544,9 +592,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.infer_pat(*pat, &ty, BindingMode::default());
|
self.infer_pat(*pat, &ty, BindingMode::default());
|
||||||
}
|
}
|
||||||
Statement::Expr(expr) => {
|
Statement::Expr(expr) => {
|
||||||
if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) {
|
self.infer_expr(*expr, &Expectation::none());
|
||||||
diverges = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -554,14 +600,22 @@ impl<'a> InferenceContext<'a> {
|
||||||
let ty = if let Some(expr) = tail {
|
let ty = if let Some(expr) = tail {
|
||||||
self.infer_expr_coerce(expr, expected)
|
self.infer_expr_coerce(expr, expected)
|
||||||
} else {
|
} else {
|
||||||
self.coerce(&Ty::unit(), expected.coercion_target());
|
// Citing rustc: if there is no explicit tail expression,
|
||||||
Ty::unit()
|
// that is typically equivalent to a tail expression
|
||||||
|
// of `()` -- except if the block diverges. In that
|
||||||
|
// case, there is no value supplied from the tail
|
||||||
|
// expression (assuming there are no other breaks,
|
||||||
|
// this implies that the type of the block will be
|
||||||
|
// `!`).
|
||||||
|
if self.diverges.is_always() {
|
||||||
|
// we don't even make an attempt at coercion
|
||||||
|
self.table.new_maybe_never_type_var()
|
||||||
|
} else {
|
||||||
|
self.coerce(&Ty::unit(), expected.coercion_target());
|
||||||
|
Ty::unit()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if diverges {
|
ty
|
||||||
Ty::simple(TypeCtor::Never)
|
|
||||||
} else {
|
|
||||||
ty
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_method_call(
|
fn infer_method_call(
|
||||||
|
|
|
@ -730,6 +730,10 @@ impl Ty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_never(&self) -> bool {
|
||||||
|
matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }))
|
||||||
|
}
|
||||||
|
|
||||||
/// If this is a `dyn Trait` type, this returns the `Trait` part.
|
/// If this is a `dyn Trait` type, this returns the `Trait` part.
|
||||||
pub fn dyn_trait_ref(&self) -> Option<&TraitRef> {
|
pub fn dyn_trait_ref(&self) -> Option<&TraitRef> {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -518,3 +518,21 @@ fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
|
||||||
|
|
||||||
assert_snapshot!(diagnostics, @"");
|
assert_snapshot!(diagnostics, @"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn break_outside_of_loop() {
|
||||||
|
let diagnostics = TestDB::with_files(
|
||||||
|
r"
|
||||||
|
//- /lib.rs
|
||||||
|
fn foo() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.diagnostics()
|
||||||
|
.0;
|
||||||
|
|
||||||
|
assert_snapshot!(diagnostics, @r###""break": break outside of loop
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -384,7 +384,7 @@ fn foo() -> u32 {
|
||||||
}
|
}
|
||||||
"#, true),
|
"#, true),
|
||||||
@r###"
|
@r###"
|
||||||
17..40 '{ ...own; }': !
|
17..40 '{ ...own; }': u32
|
||||||
23..37 'return unknown': !
|
23..37 'return unknown': !
|
||||||
30..37 'unknown': u32
|
30..37 'unknown': u32
|
||||||
"###
|
"###
|
||||||
|
@ -514,7 +514,7 @@ fn foo() {
|
||||||
27..103 '{ ... }': &u32
|
27..103 '{ ... }': &u32
|
||||||
37..82 'if tru... }': ()
|
37..82 'if tru... }': ()
|
||||||
40..44 'true': bool
|
40..44 'true': bool
|
||||||
45..82 '{ ... }': !
|
45..82 '{ ... }': ()
|
||||||
59..71 'return &1u32': !
|
59..71 'return &1u32': !
|
||||||
66..71 '&1u32': &u32
|
66..71 '&1u32': &u32
|
||||||
67..71 '1u32': u32
|
67..71 '1u32': u32
|
||||||
|
|
|
@ -197,7 +197,7 @@ fn spam() {
|
||||||
!0..6 '1isize': isize
|
!0..6 '1isize': isize
|
||||||
!0..6 '1isize': isize
|
!0..6 '1isize': isize
|
||||||
!0..6 '1isize': isize
|
!0..6 '1isize': isize
|
||||||
54..457 '{ ...!(); }': !
|
54..457 '{ ...!(); }': ()
|
||||||
88..109 'spam!(...am!())': {unknown}
|
88..109 'spam!(...am!())': {unknown}
|
||||||
115..134 'for _ ...!() {}': ()
|
115..134 'for _ ...!() {}': ()
|
||||||
119..120 '_': {unknown}
|
119..120 '_': {unknown}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use super::type_at;
|
use insta::assert_snapshot;
|
||||||
|
|
||||||
|
use super::{infer_with_mismatches, type_at};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_never1() {
|
fn infer_never1() {
|
||||||
|
@ -261,3 +263,176 @@ fn test(a: i32) {
|
||||||
);
|
);
|
||||||
assert_eq!(t, "f64");
|
assert_eq!(t, "f64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_expression_1() {
|
||||||
|
let t = infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test1() {
|
||||||
|
let x: u32 = return;
|
||||||
|
}
|
||||||
|
fn test2() {
|
||||||
|
let x: u32 = { return; };
|
||||||
|
}
|
||||||
|
fn test3() {
|
||||||
|
let x: u32 = loop {};
|
||||||
|
}
|
||||||
|
fn test4() {
|
||||||
|
let x: u32 = { loop {} };
|
||||||
|
}
|
||||||
|
fn test5() {
|
||||||
|
let x: u32 = { if true { loop {}; } else { loop {}; } };
|
||||||
|
}
|
||||||
|
fn test6() {
|
||||||
|
let x: u32 = { let y: u32 = { loop {}; }; };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert_snapshot!(t, @r###"
|
||||||
|
25..53 '{ ...urn; }': ()
|
||||||
|
35..36 'x': u32
|
||||||
|
44..50 'return': !
|
||||||
|
65..98 '{ ...; }; }': ()
|
||||||
|
75..76 'x': u32
|
||||||
|
84..95 '{ return; }': u32
|
||||||
|
86..92 'return': !
|
||||||
|
110..139 '{ ... {}; }': ()
|
||||||
|
120..121 'x': u32
|
||||||
|
129..136 'loop {}': !
|
||||||
|
134..136 '{}': ()
|
||||||
|
151..184 '{ ...} }; }': ()
|
||||||
|
161..162 'x': u32
|
||||||
|
170..181 '{ loop {} }': u32
|
||||||
|
172..179 'loop {}': !
|
||||||
|
177..179 '{}': ()
|
||||||
|
196..260 '{ ...} }; }': ()
|
||||||
|
206..207 'x': u32
|
||||||
|
215..257 '{ if t...}; } }': u32
|
||||||
|
217..255 'if tru... {}; }': u32
|
||||||
|
220..224 'true': bool
|
||||||
|
225..237 '{ loop {}; }': u32
|
||||||
|
227..234 'loop {}': !
|
||||||
|
232..234 '{}': ()
|
||||||
|
243..255 '{ loop {}; }': u32
|
||||||
|
245..252 'loop {}': !
|
||||||
|
250..252 '{}': ()
|
||||||
|
272..324 '{ ...; }; }': ()
|
||||||
|
282..283 'x': u32
|
||||||
|
291..321 '{ let ...; }; }': u32
|
||||||
|
297..298 'y': u32
|
||||||
|
306..318 '{ loop {}; }': u32
|
||||||
|
308..315 'loop {}': !
|
||||||
|
313..315 '{}': ()
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_expression_2() {
|
||||||
|
let t = infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test1() {
|
||||||
|
// should give type mismatch
|
||||||
|
let x: u32 = { loop {}; "foo" };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert_snapshot!(t, @r###"
|
||||||
|
25..98 '{ ..." }; }': ()
|
||||||
|
68..69 'x': u32
|
||||||
|
77..95 '{ loop...foo" }': &str
|
||||||
|
79..86 'loop {}': !
|
||||||
|
84..86 '{}': ()
|
||||||
|
88..93 '"foo"': &str
|
||||||
|
77..95: expected u32, got &str
|
||||||
|
88..93: expected u32, got &str
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn diverging_expression_3_break() {
|
||||||
|
let t = infer_with_mismatches(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test1() {
|
||||||
|
// should give type mismatch
|
||||||
|
let x: u32 = { loop { break; } };
|
||||||
|
}
|
||||||
|
fn test2() {
|
||||||
|
// should give type mismatch
|
||||||
|
let x: u32 = { for a in b { break; }; };
|
||||||
|
// should give type mismatch as well
|
||||||
|
let x: u32 = { for a in b {}; };
|
||||||
|
// should give type mismatch as well
|
||||||
|
let x: u32 = { for a in b { return; }; };
|
||||||
|
}
|
||||||
|
fn test3() {
|
||||||
|
// should give type mismatch
|
||||||
|
let x: u32 = { while true { break; }; };
|
||||||
|
// should give type mismatch as well -- there's an implicit break, even if it's never hit
|
||||||
|
let x: u32 = { while true {}; };
|
||||||
|
// should give type mismatch as well
|
||||||
|
let x: u32 = { while true { return; }; };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert_snapshot!(t, @r###"
|
||||||
|
25..99 '{ ...} }; }': ()
|
||||||
|
68..69 'x': u32
|
||||||
|
77..96 '{ loop...k; } }': ()
|
||||||
|
79..94 'loop { break; }': ()
|
||||||
|
84..94 '{ break; }': ()
|
||||||
|
86..91 'break': !
|
||||||
|
77..96: expected u32, got ()
|
||||||
|
79..94: expected u32, got ()
|
||||||
|
111..357 '{ ...; }; }': ()
|
||||||
|
154..155 'x': u32
|
||||||
|
163..189 '{ for ...; }; }': ()
|
||||||
|
165..186 'for a ...eak; }': ()
|
||||||
|
169..170 'a': {unknown}
|
||||||
|
174..175 'b': {unknown}
|
||||||
|
176..186 '{ break; }': ()
|
||||||
|
178..183 'break': !
|
||||||
|
240..241 'x': u32
|
||||||
|
249..267 '{ for ... {}; }': ()
|
||||||
|
251..264 'for a in b {}': ()
|
||||||
|
255..256 'a': {unknown}
|
||||||
|
260..261 'b': {unknown}
|
||||||
|
262..264 '{}': ()
|
||||||
|
318..319 'x': u32
|
||||||
|
327..354 '{ for ...; }; }': ()
|
||||||
|
329..351 'for a ...urn; }': ()
|
||||||
|
333..334 'a': {unknown}
|
||||||
|
338..339 'b': {unknown}
|
||||||
|
340..351 '{ return; }': ()
|
||||||
|
342..348 'return': !
|
||||||
|
163..189: expected u32, got ()
|
||||||
|
249..267: expected u32, got ()
|
||||||
|
327..354: expected u32, got ()
|
||||||
|
369..668 '{ ...; }; }': ()
|
||||||
|
412..413 'x': u32
|
||||||
|
421..447 '{ whil...; }; }': ()
|
||||||
|
423..444 'while ...eak; }': ()
|
||||||
|
429..433 'true': bool
|
||||||
|
434..444 '{ break; }': ()
|
||||||
|
436..441 'break': !
|
||||||
|
551..552 'x': u32
|
||||||
|
560..578 '{ whil... {}; }': ()
|
||||||
|
562..575 'while true {}': ()
|
||||||
|
568..572 'true': bool
|
||||||
|
573..575 '{}': ()
|
||||||
|
629..630 'x': u32
|
||||||
|
638..665 '{ whil...; }; }': ()
|
||||||
|
640..662 'while ...urn; }': ()
|
||||||
|
646..650 'true': bool
|
||||||
|
651..662 '{ return; }': ()
|
||||||
|
653..659 'return': !
|
||||||
|
421..447: expected u32, got ()
|
||||||
|
560..578: expected u32, got ()
|
||||||
|
638..665: expected u32, got ()
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
|
@ -179,7 +179,7 @@ fn test(a: u32, b: isize, c: !, d: &str) {
|
||||||
17..18 'b': isize
|
17..18 'b': isize
|
||||||
27..28 'c': !
|
27..28 'c': !
|
||||||
33..34 'd': &str
|
33..34 'd': &str
|
||||||
42..121 '{ ...f32; }': !
|
42..121 '{ ...f32; }': ()
|
||||||
48..49 'a': u32
|
48..49 'a': u32
|
||||||
55..56 'b': isize
|
55..56 'b': isize
|
||||||
62..63 'c': !
|
62..63 'c': !
|
||||||
|
@ -935,7 +935,7 @@ fn foo() {
|
||||||
29..33 'true': bool
|
29..33 'true': bool
|
||||||
34..51 '{ ... }': i32
|
34..51 '{ ... }': i32
|
||||||
44..45 '1': i32
|
44..45 '1': i32
|
||||||
57..80 '{ ... }': !
|
57..80 '{ ... }': i32
|
||||||
67..73 'return': !
|
67..73 'return': !
|
||||||
90..93 '_x2': i32
|
90..93 '_x2': i32
|
||||||
96..149 'if tru... }': i32
|
96..149 'if tru... }': i32
|
||||||
|
@ -951,7 +951,7 @@ fn foo() {
|
||||||
186..190 'true': bool
|
186..190 'true': bool
|
||||||
194..195 '3': i32
|
194..195 '3': i32
|
||||||
205..206 '_': bool
|
205..206 '_': bool
|
||||||
210..241 '{ ... }': !
|
210..241 '{ ... }': i32
|
||||||
224..230 'return': !
|
224..230 'return': !
|
||||||
257..260 '_x4': i32
|
257..260 '_x4': i32
|
||||||
263..320 'match ... }': i32
|
263..320 'match ... }': i32
|
||||||
|
@ -1687,7 +1687,7 @@ fn foo() -> u32 {
|
||||||
17..59 '{ ...; }; }': ()
|
17..59 '{ ...; }; }': ()
|
||||||
27..28 'x': || -> usize
|
27..28 'x': || -> usize
|
||||||
31..56 '|| -> ...n 1; }': || -> usize
|
31..56 '|| -> ...n 1; }': || -> usize
|
||||||
43..56 '{ return 1; }': !
|
43..56 '{ return 1; }': usize
|
||||||
45..53 'return 1': !
|
45..53 'return 1': !
|
||||||
52..53 '1': usize
|
52..53 '1': usize
|
||||||
"###
|
"###
|
||||||
|
@ -1706,7 +1706,7 @@ fn foo() -> u32 {
|
||||||
17..48 '{ ...; }; }': ()
|
17..48 '{ ...; }; }': ()
|
||||||
27..28 'x': || -> ()
|
27..28 'x': || -> ()
|
||||||
31..45 '|| { return; }': || -> ()
|
31..45 '|| { return; }': || -> ()
|
||||||
34..45 '{ return; }': !
|
34..45 '{ return; }': ()
|
||||||
36..42 'return': !
|
36..42 'return': !
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue