diff options
author | Florian Diebold <[email protected]> | 2020-05-08 16:36:11 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-05-08 17:15:24 +0100 |
commit | fe7bf993aa8d64668707e348f2ea69918cfda9a4 (patch) | |
tree | 9d3fc4f969dbd06d982dda6dadc6325a668e7eb1 /crates/ra_hir_ty/src | |
parent | d3eb9d8eafbebca7da95fa8a4813b92eb5080500 (diff) |
Implement better handling of divergence
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.
Diffstat (limited to 'crates/ra_hir_ty/src')
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 40 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/expr.rs | 53 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/coercion.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/macros.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/never_type.rs | 107 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/simple.rs | 10 |
7 files changed, 200 insertions, 23 deletions
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index bd4ef69a0..d3a066268 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -210,6 +210,7 @@ struct InferenceContext<'a> { | |||
210 | /// closures, but currently this is the only field that will change there, | 210 | /// closures, but currently this is the only field that will change there, |
211 | /// so it doesn't make sense. | 211 | /// so it doesn't make sense. |
212 | return_ty: Ty, | 212 | return_ty: Ty, |
213 | diverges: Diverges, | ||
213 | } | 214 | } |
214 | 215 | ||
215 | impl<'a> InferenceContext<'a> { | 216 | impl<'a> InferenceContext<'a> { |
@@ -224,6 +225,7 @@ impl<'a> InferenceContext<'a> { | |||
224 | owner, | 225 | owner, |
225 | body: db.body(owner), | 226 | body: db.body(owner), |
226 | resolver, | 227 | resolver, |
228 | diverges: Diverges::Maybe, | ||
227 | } | 229 | } |
228 | } | 230 | } |
229 | 231 | ||
@@ -666,6 +668,44 @@ impl Expectation { | |||
666 | } | 668 | } |
667 | } | 669 | } |
668 | 670 | ||
671 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
672 | enum Diverges { | ||
673 | Maybe, | ||
674 | Always, | ||
675 | } | ||
676 | |||
677 | impl Diverges { | ||
678 | fn is_always(self) -> bool { | ||
679 | self == Diverges::Always | ||
680 | } | ||
681 | } | ||
682 | |||
683 | impl std::ops::BitAnd for Diverges { | ||
684 | type Output = Self; | ||
685 | fn bitand(self, other: Self) -> Self { | ||
686 | std::cmp::min(self, other) | ||
687 | } | ||
688 | } | ||
689 | |||
690 | impl std::ops::BitOr for Diverges { | ||
691 | type Output = Self; | ||
692 | fn bitor(self, other: Self) -> Self { | ||
693 | std::cmp::max(self, other) | ||
694 | } | ||
695 | } | ||
696 | |||
697 | impl std::ops::BitAndAssign for Diverges { | ||
698 | fn bitand_assign(&mut self, other: Self) { | ||
699 | *self = *self & other; | ||
700 | } | ||
701 | } | ||
702 | |||
703 | impl std::ops::BitOrAssign for Diverges { | ||
704 | fn bitor_assign(&mut self, other: Self) { | ||
705 | *self = *self | other; | ||
706 | } | ||
707 | } | ||
708 | |||
669 | mod diagnostics { | 709 | mod diagnostics { |
670 | use hir_def::{expr::ExprId, FunctionId}; | 710 | use hir_def::{expr::ExprId, FunctionId}; |
671 | use hir_expand::diagnostics::DiagnosticSink; | 711 | use hir_expand::diagnostics::DiagnosticSink; |
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 614c352a0..f2f9883b2 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Type inference for expressions. | 1 | //! Type inference for expressions. |
2 | 2 | ||
3 | use std::iter::{repeat, repeat_with}; | 3 | use std::iter::{repeat, repeat_with}; |
4 | use std::sync::Arc; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | builtin_type::Signedness, | 7 | builtin_type::Signedness, |
@@ -21,11 +21,15 @@ use crate::{ | |||
21 | Ty, TypeCtor, Uncertain, | 21 | Ty, TypeCtor, Uncertain, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch}; | 24 | use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, Diverges}; |
25 | 25 | ||
26 | impl<'a> InferenceContext<'a> { | 26 | impl<'a> InferenceContext<'a> { |
27 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { | 27 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { |
28 | let ty = self.infer_expr_inner(tgt_expr, expected); | 28 | let ty = self.infer_expr_inner(tgt_expr, expected); |
29 | if ty.is_never() { | ||
30 | // Any expression that produces a value of type `!` must have diverged | ||
31 | self.diverges = Diverges::Always; | ||
32 | } | ||
29 | let could_unify = self.unify(&ty, &expected.ty); | 33 | let could_unify = self.unify(&ty, &expected.ty); |
30 | if !could_unify { | 34 | if !could_unify { |
31 | self.result.type_mismatches.insert( | 35 | self.result.type_mismatches.insert( |
@@ -64,11 +68,18 @@ impl<'a> InferenceContext<'a> { | |||
64 | // if let is desugared to match, so this is always simple if | 68 | // if let is desugared to match, so this is always simple if |
65 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 69 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
66 | 70 | ||
71 | let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); | ||
72 | let mut both_arms_diverge = Diverges::Always; | ||
73 | |||
67 | let then_ty = self.infer_expr_inner(*then_branch, &expected); | 74 | let then_ty = self.infer_expr_inner(*then_branch, &expected); |
75 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); | ||
68 | let else_ty = match else_branch { | 76 | let else_ty = match else_branch { |
69 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), | 77 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), |
70 | None => Ty::unit(), | 78 | None => Ty::unit(), |
71 | }; | 79 | }; |
80 | both_arms_diverge &= self.diverges; | ||
81 | |||
82 | self.diverges = condition_diverges | both_arms_diverge; | ||
72 | 83 | ||
73 | self.coerce_merge_branch(&then_ty, &else_ty) | 84 | self.coerce_merge_branch(&then_ty, &else_ty) |
74 | } | 85 | } |
@@ -132,10 +143,12 @@ impl<'a> InferenceContext<'a> { | |||
132 | // infer the body. | 143 | // infer the body. |
133 | self.coerce(&closure_ty, &expected.ty); | 144 | self.coerce(&closure_ty, &expected.ty); |
134 | 145 | ||
135 | let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone()); | 146 | let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); |
147 | let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); | ||
136 | 148 | ||
137 | self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); | 149 | self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); |
138 | 150 | ||
151 | self.diverges = prev_diverges; | ||
139 | self.return_ty = prev_ret_ty; | 152 | self.return_ty = prev_ret_ty; |
140 | 153 | ||
141 | closure_ty | 154 | closure_ty |
@@ -165,7 +178,11 @@ impl<'a> InferenceContext<'a> { | |||
165 | self.table.new_type_var() | 178 | self.table.new_type_var() |
166 | }; | 179 | }; |
167 | 180 | ||
181 | let matchee_diverges = self.diverges; | ||
182 | let mut all_arms_diverge = Diverges::Always; | ||
183 | |||
168 | for arm in arms { | 184 | for arm in arms { |
185 | self.diverges = Diverges::Maybe; | ||
169 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); | 186 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); |
170 | if let Some(guard_expr) = arm.guard { | 187 | if let Some(guard_expr) = arm.guard { |
171 | self.infer_expr( | 188 | self.infer_expr( |
@@ -175,9 +192,12 @@ impl<'a> InferenceContext<'a> { | |||
175 | } | 192 | } |
176 | 193 | ||
177 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); | 194 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); |
195 | all_arms_diverge &= self.diverges; | ||
178 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); | 196 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); |
179 | } | 197 | } |
180 | 198 | ||
199 | self.diverges = matchee_diverges | all_arms_diverge; | ||
200 | |||
181 | result_ty | 201 | result_ty |
182 | } | 202 | } |
183 | Expr::Path(p) => { | 203 | Expr::Path(p) => { |
@@ -522,7 +542,6 @@ impl<'a> InferenceContext<'a> { | |||
522 | tail: Option<ExprId>, | 542 | tail: Option<ExprId>, |
523 | expected: &Expectation, | 543 | expected: &Expectation, |
524 | ) -> Ty { | 544 | ) -> Ty { |
525 | let mut diverges = false; | ||
526 | for stmt in statements { | 545 | for stmt in statements { |
527 | match stmt { | 546 | match stmt { |
528 | Statement::Let { pat, type_ref, initializer } => { | 547 | Statement::Let { pat, type_ref, initializer } => { |
@@ -544,9 +563,7 @@ impl<'a> InferenceContext<'a> { | |||
544 | self.infer_pat(*pat, &ty, BindingMode::default()); | 563 | self.infer_pat(*pat, &ty, BindingMode::default()); |
545 | } | 564 | } |
546 | Statement::Expr(expr) => { | 565 | Statement::Expr(expr) => { |
547 | if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) { | 566 | self.infer_expr(*expr, &Expectation::none()); |
548 | diverges = true; | ||
549 | } | ||
550 | } | 567 | } |
551 | } | 568 | } |
552 | } | 569 | } |
@@ -554,14 +571,22 @@ impl<'a> InferenceContext<'a> { | |||
554 | let ty = if let Some(expr) = tail { | 571 | let ty = if let Some(expr) = tail { |
555 | self.infer_expr_coerce(expr, expected) | 572 | self.infer_expr_coerce(expr, expected) |
556 | } else { | 573 | } else { |
557 | self.coerce(&Ty::unit(), expected.coercion_target()); | 574 | // Citing rustc: if there is no explicit tail expression, |
558 | Ty::unit() | 575 | // that is typically equivalent to a tail expression |
576 | // of `()` -- except if the block diverges. In that | ||
577 | // case, there is no value supplied from the tail | ||
578 | // expression (assuming there are no other breaks, | ||
579 | // this implies that the type of the block will be | ||
580 | // `!`). | ||
581 | if self.diverges.is_always() { | ||
582 | // we don't even make an attempt at coercion | ||
583 | self.table.new_maybe_never_type_var() | ||
584 | } else { | ||
585 | self.coerce(&Ty::unit(), expected.coercion_target()); | ||
586 | Ty::unit() | ||
587 | } | ||
559 | }; | 588 | }; |
560 | if diverges { | 589 | ty |
561 | Ty::simple(TypeCtor::Never) | ||
562 | } else { | ||
563 | ty | ||
564 | } | ||
565 | } | 590 | } |
566 | 591 | ||
567 | fn infer_method_call( | 592 | fn infer_method_call( |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a6f56c661..ac0ef1bfe 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -730,6 +730,13 @@ impl Ty { | |||
730 | } | 730 | } |
731 | } | 731 | } |
732 | 732 | ||
733 | pub fn is_never(&self) -> bool { | ||
734 | match self { | ||
735 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => true, | ||
736 | _ => false, | ||
737 | } | ||
738 | } | ||
739 | |||
733 | /// If this is a `dyn Trait` type, this returns the `Trait` part. | 740 | /// If this is a `dyn Trait` type, this returns the `Trait` part. |
734 | pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { | 741 | pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { |
735 | match self { | 742 | match self { |
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index e6fb3e123..0c3a833bd 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -384,7 +384,7 @@ fn foo() -> u32 { | |||
384 | } | 384 | } |
385 | "#, true), | 385 | "#, true), |
386 | @r###" | 386 | @r###" |
387 | 17..40 '{ ...own; }': ! | 387 | 17..40 '{ ...own; }': u32 |
388 | 23..37 'return unknown': ! | 388 | 23..37 'return unknown': ! |
389 | 30..37 'unknown': u32 | 389 | 30..37 'unknown': u32 |
390 | "### | 390 | "### |
@@ -514,7 +514,7 @@ fn foo() { | |||
514 | 27..103 '{ ... }': &u32 | 514 | 27..103 '{ ... }': &u32 |
515 | 37..82 'if tru... }': () | 515 | 37..82 'if tru... }': () |
516 | 40..44 'true': bool | 516 | 40..44 'true': bool |
517 | 45..82 '{ ... }': ! | 517 | 45..82 '{ ... }': () |
518 | 59..71 'return &1u32': ! | 518 | 59..71 'return &1u32': ! |
519 | 66..71 '&1u32': &u32 | 519 | 66..71 '&1u32': &u32 |
520 | 67..71 '1u32': u32 | 520 | 67..71 '1u32': u32 |
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 07398ddcc..4c6099aa2 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs | |||
@@ -197,7 +197,7 @@ fn spam() { | |||
197 | !0..6 '1isize': isize | 197 | !0..6 '1isize': isize |
198 | !0..6 '1isize': isize | 198 | !0..6 '1isize': isize |
199 | !0..6 '1isize': isize | 199 | !0..6 '1isize': isize |
200 | 54..457 '{ ...!(); }': ! | 200 | 54..457 '{ ...!(); }': () |
201 | 88..109 'spam!(...am!())': {unknown} | 201 | 88..109 'spam!(...am!())': {unknown} |
202 | 115..134 'for _ ...!() {}': () | 202 | 115..134 'for _ ...!() {}': () |
203 | 119..120 '_': {unknown} | 203 | 119..120 '_': {unknown} |
diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs index a77209480..1721f97c5 100644 --- a/crates/ra_hir_ty/src/tests/never_type.rs +++ b/crates/ra_hir_ty/src/tests/never_type.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | use super::type_at; | 1 | use insta::assert_snapshot; |
2 | |||
3 | use super::{infer_with_mismatches, type_at}; | ||
2 | 4 | ||
3 | #[test] | 5 | #[test] |
4 | fn infer_never1() { | 6 | fn infer_never1() { |
@@ -261,3 +263,106 @@ fn test(a: i32) { | |||
261 | ); | 263 | ); |
262 | assert_eq!(t, "f64"); | 264 | assert_eq!(t, "f64"); |
263 | } | 265 | } |
266 | |||
267 | #[test] | ||
268 | fn diverging_expression_1() { | ||
269 | let t = infer_with_mismatches( | ||
270 | r#" | ||
271 | //- /main.rs | ||
272 | fn test1() { | ||
273 | let x: u32 = return; | ||
274 | } | ||
275 | fn test2() { | ||
276 | let x: u32 = { return; }; | ||
277 | } | ||
278 | fn test3() { | ||
279 | let x: u32 = loop {}; | ||
280 | } | ||
281 | fn test4() { | ||
282 | let x: u32 = { loop {} }; | ||
283 | } | ||
284 | fn test5() { | ||
285 | let x: u32 = { if true { loop {}; } else { loop {}; } }; | ||
286 | } | ||
287 | fn test6() { | ||
288 | let x: u32 = { let y: u32 = { loop {}; }; }; | ||
289 | } | ||
290 | "#, | ||
291 | true, | ||
292 | ); | ||
293 | assert_snapshot!(t, @r###" | ||
294 | 25..53 '{ ...urn; }': () | ||
295 | 35..36 'x': u32 | ||
296 | 44..50 'return': ! | ||
297 | 65..98 '{ ...; }; }': () | ||
298 | 75..76 'x': u32 | ||
299 | 84..95 '{ return; }': u32 | ||
300 | 86..92 'return': ! | ||
301 | 110..139 '{ ... {}; }': () | ||
302 | 120..121 'x': u32 | ||
303 | 129..136 'loop {}': ! | ||
304 | 134..136 '{}': () | ||
305 | 151..184 '{ ...} }; }': () | ||
306 | 161..162 'x': u32 | ||
307 | 170..181 '{ loop {} }': u32 | ||
308 | 172..179 'loop {}': ! | ||
309 | 177..179 '{}': () | ||
310 | 196..260 '{ ...} }; }': () | ||
311 | 206..207 'x': u32 | ||
312 | 215..257 '{ if t...}; } }': u32 | ||
313 | 217..255 'if tru... {}; }': u32 | ||
314 | 220..224 'true': bool | ||
315 | 225..237 '{ loop {}; }': u32 | ||
316 | 227..234 'loop {}': ! | ||
317 | 232..234 '{}': () | ||
318 | 243..255 '{ loop {}; }': u32 | ||
319 | 245..252 'loop {}': ! | ||
320 | 250..252 '{}': () | ||
321 | 272..324 '{ ...; }; }': () | ||
322 | 282..283 'x': u32 | ||
323 | 291..321 '{ let ...; }; }': u32 | ||
324 | 297..298 'y': u32 | ||
325 | 306..318 '{ loop {}; }': u32 | ||
326 | 308..315 'loop {}': ! | ||
327 | 313..315 '{}': () | ||
328 | "###); | ||
329 | } | ||
330 | |||
331 | #[test] | ||
332 | fn diverging_expression_2() { | ||
333 | let t = infer_with_mismatches( | ||
334 | r#" | ||
335 | //- /main.rs | ||
336 | fn test1() { | ||
337 | // should give type mismatch | ||
338 | let x: u32 = { loop {}; "foo" }; | ||
339 | } | ||
340 | "#, | ||
341 | true, | ||
342 | ); | ||
343 | assert_snapshot!(t, @r###" | ||
344 | 25..98 '{ ..." }; }': () | ||
345 | 68..69 'x': u32 | ||
346 | 77..95 '{ loop...foo" }': &str | ||
347 | 79..86 'loop {}': ! | ||
348 | 84..86 '{}': () | ||
349 | 88..93 '"foo"': &str | ||
350 | 77..95: expected u32, got &str | ||
351 | 88..93: expected u32, got &str | ||
352 | "###); | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn diverging_expression_3_break() { | ||
357 | let t = infer_with_mismatches( | ||
358 | r#" | ||
359 | //- /main.rs | ||
360 | fn test1() { | ||
361 | // should give type mismatch | ||
362 | let x: u32 = { loop { break; } }; | ||
363 | } | ||
364 | "#, | ||
365 | true, | ||
366 | ); | ||
367 | assert_snapshot!(t, @r###""###); | ||
368 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index e17a17900..3820175f6 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -179,7 +179,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { | |||
179 | 17..18 'b': isize | 179 | 17..18 'b': isize |
180 | 27..28 'c': ! | 180 | 27..28 'c': ! |
181 | 33..34 'd': &str | 181 | 33..34 'd': &str |
182 | 42..121 '{ ...f32; }': ! | 182 | 42..121 '{ ...f32; }': () |
183 | 48..49 'a': u32 | 183 | 48..49 'a': u32 |
184 | 55..56 'b': isize | 184 | 55..56 'b': isize |
185 | 62..63 'c': ! | 185 | 62..63 'c': ! |
@@ -935,7 +935,7 @@ fn foo() { | |||
935 | 29..33 'true': bool | 935 | 29..33 'true': bool |
936 | 34..51 '{ ... }': i32 | 936 | 34..51 '{ ... }': i32 |
937 | 44..45 '1': i32 | 937 | 44..45 '1': i32 |
938 | 57..80 '{ ... }': ! | 938 | 57..80 '{ ... }': i32 |
939 | 67..73 'return': ! | 939 | 67..73 'return': ! |
940 | 90..93 '_x2': i32 | 940 | 90..93 '_x2': i32 |
941 | 96..149 'if tru... }': i32 | 941 | 96..149 'if tru... }': i32 |
@@ -951,7 +951,7 @@ fn foo() { | |||
951 | 186..190 'true': bool | 951 | 186..190 'true': bool |
952 | 194..195 '3': i32 | 952 | 194..195 '3': i32 |
953 | 205..206 '_': bool | 953 | 205..206 '_': bool |
954 | 210..241 '{ ... }': ! | 954 | 210..241 '{ ... }': i32 |
955 | 224..230 'return': ! | 955 | 224..230 'return': ! |
956 | 257..260 '_x4': i32 | 956 | 257..260 '_x4': i32 |
957 | 263..320 'match ... }': i32 | 957 | 263..320 'match ... }': i32 |
@@ -1687,7 +1687,7 @@ fn foo() -> u32 { | |||
1687 | 17..59 '{ ...; }; }': () | 1687 | 17..59 '{ ...; }; }': () |
1688 | 27..28 'x': || -> usize | 1688 | 27..28 'x': || -> usize |
1689 | 31..56 '|| -> ...n 1; }': || -> usize | 1689 | 31..56 '|| -> ...n 1; }': || -> usize |
1690 | 43..56 '{ return 1; }': ! | 1690 | 43..56 '{ return 1; }': usize |
1691 | 45..53 'return 1': ! | 1691 | 45..53 'return 1': ! |
1692 | 52..53 '1': usize | 1692 | 52..53 '1': usize |
1693 | "### | 1693 | "### |
@@ -1706,7 +1706,7 @@ fn foo() -> u32 { | |||
1706 | 17..48 '{ ...; }; }': () | 1706 | 17..48 '{ ...; }; }': () |
1707 | 27..28 'x': || -> () | 1707 | 27..28 'x': || -> () |
1708 | 31..45 '|| { return; }': || -> () | 1708 | 31..45 '|| { return; }': || -> () |
1709 | 34..45 '{ return; }': ! | 1709 | 34..45 '{ return; }': () |
1710 | 36..42 'return': ! | 1710 | 36..42 'return': ! |
1711 | "### | 1711 | "### |
1712 | ); | 1712 | ); |