Skip to content

Commit 3231e54

Browse files
committed
Always replace the never type by a diverging type variable
Previously, the never type will be replaced by a diverging type variable (generally) only if some coercion occurs. It can cause inconsistent behaviors like ```rust return.foo(); //~ ERROR no method named `foo` found for type `!` { return }.foo(); //~ ERROR type annotations needed ``` ```rust let a = (return, ); // The type is `(!, )`. let a = ({ return }, ); // The type is `(_, )`. let a: (_, ) = (return, ); // The type is `(_, )`. ``` With this commit, the never type will be replaced by a diverging type variable just at the end of the type check for every expression, even if no coercion occurs. Thus the problems above get solved and the consistency should be improved.
1 parent 69b352e commit 3231e54

33 files changed

+286
-159
lines changed

compiler/rustc_typeck/src/check/_match.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
555555
if let Some(m) = contains_ref_bindings {
556556
self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m))
557557
} else if no_arms {
558-
self.check_expr(scrut)
558+
// The hint for never type is a little hacky, but it will make
559+
// `match never {}` work even without `never_type_fallback`.
560+
// We can remove it once the feature `never_type_fallback` gets
561+
// stabilized.
562+
self.check_expr_with_hint(scrut, self.tcx.types.never)
559563
} else {
560564
// ...but otherwise we want to use any supertype of the
561565
// scrutinee. This is sort of a workaround, see note (*) in

compiler/rustc_typeck/src/check/coercion.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -1022,8 +1022,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10221022

10231023
// First try to coerce the new expression to the type of the previous ones,
10241024
// but only if the new expression has no coercion already applied to it.
1025-
let mut first_error = None;
1026-
if !self.typeck_results.borrow().adjustments().contains_key(new.hir_id) {
1025+
let first_try = match self.typeck_results.borrow().expr_adjustments(new) {
1026+
&[] | &[Adjustment { kind: Adjust::NeverToAny, .. }] => true,
1027+
_ => false,
1028+
};
1029+
let first_error = if first_try {
10271030
let result = self.commit_if_ok(|_| coerce.coerce(new_ty, prev_ty));
10281031
match result {
10291032
Ok(ok) => {
@@ -1035,9 +1038,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10351038
);
10361039
return Ok(target);
10371040
}
1038-
Err(e) => first_error = Some(e),
1041+
Err(e) => Some(e),
10391042
}
1040-
}
1043+
} else {
1044+
None
1045+
};
10411046

10421047
// Then try to coerce the previous expressions to the type of the new one.
10431048
// This requires ensuring there are no coercions applied to *any* of the

compiler/rustc_typeck/src/check/expr.rs

+38-20
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6868
extend_err: impl Fn(&mut DiagnosticBuilder<'_>),
6969
) -> Ty<'tcx> {
7070
let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool);
71-
let mut ty = self.check_expr_with_expectation(expr, expected);
72-
73-
// While we don't allow *arbitrary* coercions here, we *do* allow
74-
// coercions from ! to `expected`.
75-
if ty.is_never() {
76-
assert!(
77-
!self.typeck_results.borrow().adjustments().contains_key(expr.hir_id),
78-
"expression with never type wound up being adjusted"
79-
);
80-
let adj_ty = self.next_diverging_ty_var(TypeVariableOrigin {
81-
kind: TypeVariableOriginKind::AdjustmentType,
82-
span: expr.span,
83-
});
84-
self.apply_adjustments(
85-
expr,
86-
vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }],
87-
);
88-
ty = adj_ty;
89-
}
71+
let ty = self.check_expr_with_expectation(expr, expected);
9072

9173
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
9274
let expr = expr.peel_drop_temps();
@@ -216,7 +198,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
216198
debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id));
217199
debug!("... {:?}, expected is {:?}", ty, expected);
218200

219-
ty
201+
// Convert the never type to a diverging type variable.
202+
// Overall it helps to improve the consistency. We expect that we can
203+
// have the same behaviour for `return.foo()` and `{ return }.foo()`.
204+
if ty.is_never() {
205+
assert!(
206+
!self.typeck_results.borrow().adjustments().contains_key(expr.hir_id),
207+
"expression with never type wound up being adjusted"
208+
);
209+
210+
let expected_ty = match expected {
211+
ExpectHasType(target_ty) => Some(target_ty),
212+
ExpectCastableToType(target_ty) => Some(target_ty),
213+
_ => None,
214+
};
215+
216+
// Mirco-optimization: No need to create a diverging type variable
217+
// if the target type is known.
218+
let target_ty = expected_ty
219+
.map(|target_ty| self.infcx.shallow_resolve(target_ty))
220+
.filter(|target_ty| !target_ty.is_ty_var())
221+
.unwrap_or_else(|| {
222+
self.next_diverging_ty_var(TypeVariableOrigin {
223+
kind: TypeVariableOriginKind::AdjustmentType,
224+
span: expr.span,
225+
})
226+
});
227+
if !target_ty.is_never() {
228+
self.apply_adjustments(
229+
expr,
230+
vec![Adjustment { kind: Adjust::NeverToAny, target: target_ty }],
231+
);
232+
}
233+
234+
target_ty
235+
} else {
236+
ty
237+
}
220238
}
221239

222240
fn check_expr_kind(

src/test/mir-opt/inline/inline_diverging.f.Inline.diff

+5-3
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@
44
fn f() -> () {
55
let mut _0: (); // return place in scope 0 at $DIR/inline-diverging.rs:7:12: 7:12
66
let mut _1: !; // in scope 0 at $DIR/inline-diverging.rs:7:12: 9:2
7-
let _2: !; // in scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
8-
+ let mut _3: !; // in scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
7+
let _2: (); // in scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
8+
let mut _3: !; // in scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
9+
+ let mut _4: !; // in scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
910
+ scope 1 (inlined sleep) { // at $DIR/inline-diverging.rs:8:5: 8:12
1011
+ }
1112

1213
bb0: {
1314
StorageLive(_2); // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
15+
StorageLive(_3); // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
1416
- sleep(); // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
1517
- // mir::Constant
1618
- // + span: $DIR/inline-diverging.rs:8:5: 8:10
1719
- // + literal: Const { ty: fn() -> ! {sleep}, val: Value(Scalar(<ZST>)) }
18-
+ StorageLive(_3); // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
20+
+ StorageLive(_4); // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
1921
+ goto -> bb1; // scope 0 at $DIR/inline-diverging.rs:8:5: 8:12
2022
+ }
2123
+

src/test/mir-opt/inline/inline_diverging.g.Inline.diff

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
let mut _3: i32; // in scope 0 at $DIR/inline-diverging.rs:13:8: 13:9
99
let mut _4: i32; // in scope 0 at $DIR/inline-diverging.rs:14:9: 14:10
1010
let mut _5: !; // in scope 0 at $DIR/inline-diverging.rs:15:12: 17:6
11-
let _6: !; // in scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
12-
+ let mut _7: !; // in scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
11+
let _6: (); // in scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
12+
let mut _7: !; // in scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
13+
+ let mut _8: !; // in scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
1314
+ scope 1 (inlined panic) { // at $DIR/inline-diverging.rs:16:9: 16:16
1415
+ }
1516

@@ -33,8 +34,9 @@
3334

3435
bb2: {
3536
StorageLive(_6); // scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
37+
StorageLive(_7); // scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
3638
- panic(); // scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
37-
+ StorageLive(_7); // scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
39+
+ StorageLive(_8); // scope 0 at $DIR/inline-diverging.rs:16:9: 16:16
3840
+ begin_panic::<&str>(const "explicit panic"); // scope 1 at $DIR/inline-diverging.rs:16:9: 16:16
3941
// mir::Constant
4042
- // + span: $DIR/inline-diverging.rs:16:9: 16:14

src/test/mir-opt/issue_38669.main.SimplifyCfg-initial.after.mir

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ fn main() -> () {
77
let _3: (); // in scope 0 at $DIR/issue-38669.rs:7:9: 9:10
88
let mut _4: bool; // in scope 0 at $DIR/issue-38669.rs:7:12: 7:24
99
let mut _5: !; // in scope 0 at $DIR/issue-38669.rs:7:25: 9:10
10+
let _6: (); // in scope 0 at $DIR/issue-38669.rs:8:13: 8:18
11+
let mut _7: !; // in scope 0 at $DIR/issue-38669.rs:8:13: 8:18
1012
scope 1 {
1113
debug should_break => _1; // in scope 1 at $DIR/issue-38669.rs:5:9: 5:25
1214
}
@@ -30,7 +32,9 @@ fn main() -> () {
3032
}
3133

3234
bb3: {
35+
StorageLive(_6); // scope 1 at $DIR/issue-38669.rs:8:13: 8:18
3336
_0 = const (); // scope 1 at $DIR/issue-38669.rs:8:13: 8:18
37+
StorageDead(_6); // scope 1 at $DIR/issue-38669.rs:8:18: 8:19
3438
StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10
3539
StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10
3640
StorageDead(_1); // scope 0 at $DIR/issue-38669.rs:12:1: 12:2

src/test/mir-opt/issue_72181_1.main.mir_map.0.mir

+8-5
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ fn main() -> () {
99
let mut _1: !; // in scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2
1010
let _2: Void as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
1111
let mut _3: (); // in scope 0 at $DIR/issue-72181-1.rs:17:41: 17:43
12-
let _4: !; // in scope 0 at $DIR/issue-72181-1.rs:20:5: 20:9
13-
let mut _5: Void; // in scope 0 at $DIR/issue-72181-1.rs:20:7: 20:8
12+
let _4: (); // in scope 0 at $DIR/issue-72181-1.rs:20:5: 20:9
13+
let mut _5: !; // in scope 0 at $DIR/issue-72181-1.rs:20:5: 20:9
14+
let mut _6: Void; // in scope 0 at $DIR/issue-72181-1.rs:20:7: 20:8
1415
scope 1 {
1516
debug v => _2; // in scope 1 at $DIR/issue-72181-1.rs:16:9: 16:10
1617
}
@@ -32,15 +33,17 @@ fn main() -> () {
3233
FakeRead(ForLet(None), _2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10
3334
AscribeUserType(_2, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/issue-72181-1.rs:16:12: 16:16
3435
StorageLive(_4); // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
35-
StorageLive(_5); // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
36-
_5 = move _2; // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
37-
f(move _5) -> bb4; // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
36+
StorageLive(_5); // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
37+
StorageLive(_6); // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
38+
_6 = move _2; // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8
39+
f(move _6) -> bb4; // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9
3840
// mir::Constant
3941
// + span: $DIR/issue-72181-1.rs:20:5: 20:6
4042
// + literal: Const { ty: fn(Void) -> ! {f}, val: Value(Scalar(<ZST>)) }
4143
}
4244

4345
bb2: {
46+
StorageDead(_6); // scope 1 at $DIR/issue-72181-1.rs:20:8: 20:9
4447
StorageDead(_5); // scope 1 at $DIR/issue-72181-1.rs:20:8: 20:9
4548
StorageDead(_4); // scope 1 at $DIR/issue-72181-1.rs:20:9: 20:10
4649
StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:21:1: 21:2

src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.32bit.diff

+20-18
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,22 @@
1919
let mut _17: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
2020
let mut _18: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
2121
let mut _19: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
22-
let _21: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
23-
let mut _22: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
24-
let mut _23: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
25-
let _24: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
26-
let mut _25: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
27-
let _26: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
28-
let mut _27: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
22+
let _21: (); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
23+
let mut _22: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
24+
let mut _23: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
25+
let mut _24: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
26+
let _25: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
27+
let mut _26: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
28+
let _27: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
29+
let mut _28: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
2930
scope 1 {
3031
debug split => _1; // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14
3132
let _6: std::option::Option<i32>; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14
3233
scope 3 {
3334
debug _prev => _6; // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14
3435
let _13: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
3536
let _14: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
36-
let mut _28: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
37+
let mut _29: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
3738
scope 4 {
3839
debug left_val => _13; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
3940
debug right_val => _14; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
@@ -81,14 +82,14 @@
8182
StorageLive(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
8283
_10 = &_1; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
8384
StorageLive(_11); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
84-
_28 = const main::promoted[0]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
85+
_29 = const main::promoted[0]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
8586
// ty::Const
8687
// + ty: &i32
8788
// + val: Unevaluated(main, [], Some(promoted[0]))
8889
// mir::Constant
8990
// + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
9091
// + literal: Const { ty: &i32, val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main), const_param_did: None }, substs: [], promoted: Some(promoted[0]) }) }
91-
_11 = _28; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
92+
_11 = _29; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
9293
(_9.0: &i32) = move _10; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
9394
(_9.1: &i32) = move _11; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
9495
StorageDead(_11); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
@@ -116,24 +117,25 @@
116117
discriminant(_20) = 0; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
117118
StorageLive(_21); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
118119
StorageLive(_22); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
119-
_22 = const core::panicking::AssertKind::Eq; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
120+
StorageLive(_23); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
121+
_23 = const core::panicking::AssertKind::Eq; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
120122
// ty::Const
121123
// + ty: core::panicking::AssertKind
122124
// + val: Value(Scalar(0x00))
123125
// mir::Constant
124126
// + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
125127
// + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) }
126-
StorageLive(_23); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
127128
StorageLive(_24); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
128-
_24 = _13; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
129-
_23 = _24; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
130129
StorageLive(_25); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
130+
_25 = _13; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
131+
_24 = _25; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
131132
StorageLive(_26); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
132-
_26 = _14; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
133-
_25 = _26; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
134133
StorageLive(_27); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
135-
discriminant(_27) = 0; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
136-
core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _23, move _25, move _27); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
134+
_27 = _14; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
135+
_26 = _27; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
136+
StorageLive(_28); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
137+
discriminant(_28) = 0; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
138+
core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _24, move _26, move _28); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
137139
// mir::Constant
138140
// + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
139141
// + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r i32, &'s i32, std::option::Option<std::fmt::Arguments<'t0>>) -> ! {core::panicking::assert_failed::<i32, i32>}, val: Value(Scalar(<ZST>)) }

0 commit comments

Comments
 (0)