Skip to content

Commit d918387

Browse files
committed
Allow early fallback of diverging type variables in typeck
Previously, we issue "type annotations needed" when types must be known at some point but the resolution failed, even if the type variables are just some diverging ones. A typical example is `{ return }.foo()`. With this commit, the information about diverging is recorded in the unification table, so that we can check whether performing the fallback affects other non-diverging type variables. If it doesn't, we will safely perform the fallback and we won't issue "type annotations needed" anymore. Note lots of auxiliary type variables should be ignored during the check, which is done with the help of `TypeVariableOriginKind`. As a result, "type annotations needed" will be issued for ```rust let a = return; { if true { a } else { return } }.foo(); ``` but not for ```rust let a: ! = return; { if true { a } else { return } }.foo(); ```
1 parent 3231e54 commit d918387

19 files changed

+239
-62
lines changed

compiler/rustc_infer/src/infer/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
12751275
}
12761276
}
12771277

1278+
/// Returns `false` if all non-auxiliary type variables unified with
1279+
/// `vid` is diverging. Returns `true` otherwise.
1280+
pub fn probe_ty_diverging(&self, vid: TyVid) -> bool {
1281+
let mut inner = self.inner.borrow_mut();
1282+
inner.type_variables().var_diverges_with_unification(vid)
1283+
}
1284+
12781285
/// Resolve any type variables found in `value` -- but only one
12791286
/// level. So, if the variable `?X` is bound to some type
12801287
/// `Foo<?Y>`, then this would return `Foo<?Y>` (but `?Y` may

compiler/rustc_infer/src/infer/type_variable.rs

+100-8
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_data_structures::undo_log::{Rollback, UndoLogs};
1616
/// Represents a single undo-able action that affects a type inference variable.
1717
pub(crate) enum UndoLog<'tcx> {
1818
EqRelation(sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>),
19-
SubRelation(sv::UndoLog<ut::Delegate<ty::TyVid>>),
19+
SubRelation(sv::UndoLog<ut::Delegate<TyVidSubKey>>),
2020
Values(sv::UndoLog<Delegate>),
2121
}
2222

@@ -28,8 +28,8 @@ impl<'tcx> From<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for UndoLog<'tcx> {
2828
}
2929

3030
/// Convert from a specific kind of undo to the more general UndoLog
31-
impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::TyVid>>> for UndoLog<'tcx> {
32-
fn from(l: sv::UndoLog<ut::Delegate<ty::TyVid>>) -> Self {
31+
impl<'tcx> From<sv::UndoLog<ut::Delegate<TyVidSubKey>>> for UndoLog<'tcx> {
32+
fn from(l: sv::UndoLog<ut::Delegate<TyVidSubKey>>) -> Self {
3333
UndoLog::SubRelation(l)
3434
}
3535
}
@@ -83,7 +83,7 @@ pub struct TypeVariableStorage<'tcx> {
8383
/// This is reasonable because, in Rust, subtypes have the same
8484
/// "skeleton" and hence there is no possible type such that
8585
/// (e.g.) `Box<?3> <: ?3` for any `?3`.
86-
sub_relations: ut::UnificationTableStorage<ty::TyVid>,
86+
sub_relations: ut::UnificationTableStorage<TyVidSubKey>,
8787
}
8888

8989
pub struct TypeVariableTable<'a, 'tcx> {
@@ -169,6 +169,16 @@ impl<'tcx> TypeVariableStorage<'tcx> {
169169
}
170170

171171
impl<'tcx> TypeVariableTable<'_, 'tcx> {
172+
/// Returns `false` if all non-auxiliary type variables unified with
173+
/// `vid` is diverging. Returns `true` otherwise.
174+
///
175+
/// Precondition: `vid` should be unknown.
176+
pub fn var_diverges_with_unification(&mut self, vid: ty::TyVid) -> bool {
177+
debug_assert!(self.probe(vid).is_unknown());
178+
let kind = self.sub_relations().inlined_probe_value(vid);
179+
matches!(kind, TyVarUnifiedDiverging::Yes)
180+
}
181+
172182
/// Returns the diverges flag given when `vid` was created.
173183
///
174184
/// Note that this function does not return care whether
@@ -243,8 +253,9 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
243253
) -> ty::TyVid {
244254
let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
245255

246-
let sub_key = self.sub_relations().new_key(());
247-
assert_eq!(eq_key.vid, sub_key);
256+
let diverging_kind = TyVarUnifiedDiverging::from(diverging, origin.kind);
257+
let sub_key = self.sub_relations().new_key(diverging_kind);
258+
assert_eq!(eq_key.vid, sub_key.vid);
248259

249260
let index = self.values().push(TypeVariableData { origin, diverging });
250261
assert_eq!(eq_key.vid.index, index as u32);
@@ -279,7 +290,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
279290
///
280291
/// exists X. (a <: X || X <: a) && (b <: X || X <: b)
281292
pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
282-
self.sub_relations().find(vid)
293+
self.sub_relations().find(vid).vid
283294
}
284295

285296
/// Returns `true` if `a` and `b` have same "sub-root" (i.e., exists some
@@ -326,7 +337,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
326337
}
327338

328339
#[inline]
329-
fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> {
340+
fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidSubKey> {
330341
self.storage.sub_relations.with_log(self.undo_log)
331342
}
332343

@@ -443,3 +454,84 @@ impl<'tcx> ut::UnifyValue for TypeVariableValue<'tcx> {
443454
}
444455
}
445456
}
457+
458+
///////////////////////////////////////////////////////////////////////////
459+
460+
/// These structs (a newtyped TyVid) are used as the unification key
461+
/// for the `sub_relations`; they carry a `TyVarUnifiedDiverging`
462+
/// along with them.
463+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
464+
pub(crate) struct TyVidSubKey {
465+
vid: ty::TyVid,
466+
}
467+
468+
/// This enum denotes whether unified type variables are all diverging
469+
/// variables. Note auxiliary type variables (guessed with the help of
470+
/// `TypeVariableOriginKind`) should be ignored.
471+
#[derive(Copy, Clone, Debug)]
472+
pub enum TyVarUnifiedDiverging {
473+
/// All unified type variables are diverging.
474+
Yes,
475+
/// Some unified type variable are not diverging.
476+
No,
477+
/// We don't know the final result at all because we haven't seen
478+
/// any non-auxiliary type variables yet.
479+
Maybe,
480+
}
481+
482+
impl From<ty::TyVid> for TyVidSubKey {
483+
fn from(vid: ty::TyVid) -> Self {
484+
TyVidSubKey { vid }
485+
}
486+
}
487+
488+
impl ut::UnifyKey for TyVidSubKey {
489+
type Value = TyVarUnifiedDiverging;
490+
fn index(&self) -> u32 {
491+
self.vid.index
492+
}
493+
fn from_index(i: u32) -> Self {
494+
TyVidSubKey::from(ty::TyVid { index: i })
495+
}
496+
fn tag() -> &'static str {
497+
"TyVidSubKey"
498+
}
499+
}
500+
501+
impl ut::UnifyValue for TyVarUnifiedDiverging {
502+
type Error = ut::NoError;
503+
504+
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, ut::NoError> {
505+
match (*value1, *value2) {
506+
// Auxiliary type variables should be ignored.
507+
(TyVarUnifiedDiverging::Maybe, other) => Ok(other),
508+
(other, TyVarUnifiedDiverging::Maybe) => Ok(other),
509+
510+
// We've found some non-diverging type variables.
511+
(TyVarUnifiedDiverging::No, _) => Ok(TyVarUnifiedDiverging::No),
512+
(_, TyVarUnifiedDiverging::No) => Ok(TyVarUnifiedDiverging::No),
513+
514+
// All type variables are diverging yet.
515+
(TyVarUnifiedDiverging::Yes, TyVarUnifiedDiverging::Yes) => {
516+
Ok(TyVarUnifiedDiverging::Yes)
517+
}
518+
}
519+
}
520+
}
521+
522+
impl TyVarUnifiedDiverging {
523+
#[inline]
524+
fn from(diverging: bool, origin: TypeVariableOriginKind) -> Self {
525+
if diverging {
526+
return TyVarUnifiedDiverging::Yes;
527+
}
528+
529+
// FIXME: Is it a complete list? Probably not.
530+
match origin {
531+
TypeVariableOriginKind::MiscVariable | TypeVariableOriginKind::LatticeVariable => {
532+
TyVarUnifiedDiverging::Maybe
533+
}
534+
_ => TyVarUnifiedDiverging::No,
535+
}
536+
}
537+
}

compiler/rustc_infer/src/infer/undo_log.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl_from! {
4545
TypeVariables(type_variable::UndoLog<'tcx>),
4646

4747
TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
48-
TypeVariables(sv::UndoLog<ut::Delegate<ty::TyVid>>),
48+
TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidSubKey>>),
4949
TypeVariables(sv::UndoLog<type_variable::Delegate>),
5050
TypeVariables(type_variable::Instantiate),
5151

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

+37-10
Original file line numberDiff line numberDiff line change
@@ -1567,17 +1567,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15671567
/// Numeric inference variables may be left unresolved.
15681568
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
15691569
let ty = self.resolve_vars_with_obligations(ty);
1570-
if !ty.is_ty_var() {
1571-
ty
1570+
if let ty::Infer(ty::TyVar(vid)) = ty.kind() {
1571+
// If we get a type variable here, we may not want to issue "type
1572+
// annotations needed". For example, the code can be something like
1573+
// `panic!().foo()` or `{ return }.foo()`.
1574+
//
1575+
// However, we must issue the error message if we found the type
1576+
// variable is related to some non-auxiliary non-diverging ones.
1577+
//
1578+
// We'll issue the error message for this
1579+
// ```
1580+
// let a = return;
1581+
// { if true { a } else { return } }.foo();
1582+
// ```
1583+
// but we won't for this
1584+
// ```
1585+
// let a: ! = return;
1586+
// { if true { a } else { return } }.foo();
1587+
// ```
1588+
1589+
let new_ty = if self.infcx.probe_ty_diverging(*vid) {
1590+
if self.tcx.features().never_type_fallback {
1591+
self.tcx.types.never
1592+
} else {
1593+
self.tcx.types.unit
1594+
}
1595+
} else {
1596+
if !self.is_tainted_by_errors() {
1597+
self.emit_inference_failure_err((**self).body_id, sp, ty.into(), vec![], E0282)
1598+
.note("type must be known at this point")
1599+
.emit();
1600+
}
1601+
self.tcx.ty_error()
1602+
};
1603+
1604+
self.demand_suptype(sp, new_ty, ty);
1605+
new_ty
15721606
} else {
1573-
if !self.is_tainted_by_errors() {
1574-
self.emit_inference_failure_err((**self).body_id, sp, ty.into(), vec![], E0282)
1575-
.note("type must be known at this point")
1576-
.emit();
1577-
}
1578-
let err = self.tcx.ty_error();
1579-
self.demand_suptype(sp, err, ty);
1580-
err
1607+
ty
15811608
}
15821609
}
15831610

src/test/ui/asm/type-check-2.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// only-x86_64
22

3-
#![feature(asm, global_asm, repr_simd, never_type)]
3+
#![feature(asm, global_asm, repr_simd, never_type, never_type_fallback)]
44

55
#[repr(simd)]
66
struct SimdNonCopy(f32, f32, f32, f32);
@@ -86,9 +86,8 @@ fn main() {
8686

8787
// Type checks ignore never type
8888

89-
// FIXME: ERROR type annotations needed
90-
// let u: ! = unreachable!();
91-
// asm!("{}", in(reg) u);
89+
let u: ! = unreachable!();
90+
asm!("{}", in(reg) u);
9291
}
9392
}
9493

src/test/ui/index-bot.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
fn main() {
2-
(return)[0]; //~ ERROR type annotations needed
2+
(return)[0]; //~ ERROR cannot index into a value of type `()`
33
}

src/test/ui/index-bot.stderr

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
error[E0282]: type annotations needed
1+
error[E0608]: cannot index into a value of type `()`
22
--> $DIR/index-bot.rs:2:5
33
|
44
LL | (return)[0];
5-
| ^^^^^^^^ cannot infer type
6-
|
7-
= note: type must be known at this point
5+
| ^^^^^^^^^^^ help: to access tuple elements, use: `(return).0`
86

97
error: aborting due to previous error
108

11-
For more information about this error, try `rustc --explain E0282`.
9+
For more information about this error, try `rustc --explain E0608`.

src/test/ui/issues/issue-13847.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
fn main() {
2-
return.is_failure //~ ERROR type annotations needed
2+
return.is_failure //~ ERROR no field `is_failure` on type `()`
33
}

src/test/ui/issues/issue-13847.stderr

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
error[E0282]: type annotations needed
2-
--> $DIR/issue-13847.rs:2:5
1+
error[E0609]: no field `is_failure` on type `()`
2+
--> $DIR/issue-13847.rs:2:12
33
|
44
LL | return.is_failure
5-
| ^^^^^^ cannot infer type
6-
|
7-
= note: type must be known at this point
5+
| ^^^^^^^^^^
86

97
error: aborting due to previous error
108

11-
For more information about this error, try `rustc --explain E0282`.
9+
For more information about this error, try `rustc --explain E0609`.

src/test/ui/issues/issue-15207.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
fn main() {
22
loop {
3-
break.push(1) //~ ERROR type annotations needed
3+
break.push(1) //~ ERROR no method named `push` found for unit type `()`
44
;
55
}
66
}

src/test/ui/issues/issue-15207.stderr

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
error[E0282]: type annotations needed
2-
--> $DIR/issue-15207.rs:3:9
1+
error[E0599]: no method named `push` found for unit type `()` in the current scope
2+
--> $DIR/issue-15207.rs:3:15
33
|
44
LL | break.push(1)
5-
| ^^^^^ cannot infer type
6-
|
7-
= note: type must be known at this point
5+
| ^^^^ method not found in `()`
86

97
error: aborting due to previous error
108

11-
For more information about this error, try `rustc --explain E0282`.
9+
For more information about this error, try `rustc --explain E0599`.

src/test/ui/issues/issue-15965.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
fn main() {
22
return
33
{ return () }
4-
//~^ ERROR type annotations needed [E0282]
4+
//~^ ERROR expected function, found `_`
55
()
66
;
77
}

src/test/ui/issues/issue-15965.stderr

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
error[E0282]: type annotations needed
1+
error[E0618]: expected function, found `_`
22
--> $DIR/issue-15965.rs:3:9
33
|
4-
LL | / { return () }
4+
LL | { return () }
5+
| _________-^^^^^^^^^^^^
56
LL | |
67
LL | | ()
7-
| |______^ cannot infer type
8-
|
9-
= note: type must be known at this point
8+
| |______- call expression requires function
109

1110
error: aborting due to previous error
1211

13-
For more information about this error, try `rustc --explain E0282`.
12+
For more information about this error, try `rustc --explain E0618`.

src/test/ui/issues/issue-17373.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
fn main() {
2-
*return //~ ERROR type annotations needed
2+
*return //~ ERROR type `()` cannot be dereferenced
33
;
44
}

src/test/ui/issues/issue-17373.stderr

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
error[E0282]: type annotations needed
1+
error[E0614]: type `()` cannot be dereferenced
22
--> $DIR/issue-17373.rs:2:5
33
|
44
LL | *return
5-
| ^^^^^^^ cannot infer type
6-
|
7-
= note: type must be known at this point
5+
| ^^^^^^^
86

97
error: aborting due to previous error
108

11-
For more information about this error, try `rustc --explain E0282`.
9+
For more information about this error, try `rustc --explain E0614`.

src/test/ui/issues/issue-18532.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
// into it.
44

55
fn main() {
6-
(return)((),()); //~ ERROR type annotations needed
6+
(return)((),()); //~ ERROR expected function, found `_`
77
}

src/test/ui/issues/issue-18532.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
error[E0282]: type annotations needed
1+
error[E0618]: expected function, found `_`
22
--> $DIR/issue-18532.rs:6:5
33
|
44
LL | (return)((),());
5-
| ^^^^^^^^^^^^^^^ cannot infer type
6-
|
7-
= note: type must be known at this point
5+
| ^^^^^^^^-------
6+
| |
7+
| call expression requires function
88

99
error: aborting due to previous error
1010

11-
For more information about this error, try `rustc --explain E0282`.
11+
For more information about this error, try `rustc --explain E0618`.

0 commit comments

Comments
 (0)