Skip to content

Commit 6b0c545

Browse files
authored
Rollup merge of rust-lang#66431 - Aaron1011:fix/opaque-type-infer, r=varkor
Fix 'type annotations needed' error with opaque types Related: rust-lang#66426 This commit adds handling for opaque types during inference variable fallback. Type variables generated from the instantiation of opaque types now fallback to the opaque type itself. Normally, the type variable for an instantiated opaque type is either unified with the concrete type, or with the opaque type itself (e.g when a function returns an opaque type by calling another function). However, it's possible for the type variable to be left completely unconstrained. This can occur in code like this: ```rust pub type Foo = impl Copy; fn produce() -> Option<Foo> { None } ``` Here, we'll instantatiate the `Foo` in `Option<Foo>` to a fresh type variable, but we will never unify it with anything due to the fact that we return a `None`. This results in the error message: ``` type annotations needed: cannot resolve `_: std::marker::Copy ``` pointing at `pub type Foo = impl Copy`. This message is not only confusing, it's incorrect. When an opaque type inference variable is completely unconstrained, we can always fall back to using the opaque type itself. This effectively turns that particular use of the opaque type into a non-defining use, even if it appears in a defining scope.
2 parents 0456a83 + a11abe0 commit 6b0c545

File tree

7 files changed

+205
-66
lines changed

7 files changed

+205
-66
lines changed

src/librustc/infer/opaque_types/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ pub type OpaqueTypeMap<'tcx> = DefIdMap<OpaqueTypeDecl<'tcx>>;
2424
/// appear in the return type).
2525
#[derive(Copy, Clone, Debug)]
2626
pub struct OpaqueTypeDecl<'tcx> {
27+
28+
/// The opaque type (`ty::Opaque`) for this declaration.
29+
pub opaque_type: Ty<'tcx>,
30+
2731
/// The substitutions that we apply to the opaque type that this
2832
/// `impl Trait` desugars to. e.g., if:
2933
///
@@ -1150,6 +1154,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
11501154
self.opaque_types.insert(
11511155
def_id,
11521156
OpaqueTypeDecl {
1157+
opaque_type: ty,
11531158
substs,
11541159
definition_span,
11551160
concrete_ty: ty_var,

src/librustc_typeck/check/mod.rs

+107-4
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ use crate::TypeAndSubsts;
147147
use crate::lint;
148148
use crate::util::captures::Captures;
149149
use crate::util::common::{ErrorReported, indenter};
150-
use crate::util::nodemap::{DefIdMap, DefIdSet, FxHashSet, HirIdMap};
150+
use crate::util::nodemap::{DefIdMap, DefIdSet, FxHashMap, FxHashSet, HirIdMap};
151151

152152
pub use self::Expectation::*;
153153
use self::autoderef::Autoderef;
@@ -231,6 +231,13 @@ pub struct Inherited<'a, 'tcx> {
231231
// 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
232232
opaque_types: RefCell<DefIdMap<OpaqueTypeDecl<'tcx>>>,
233233

234+
/// A map from inference variables created from opaque
235+
/// type instantiations (`ty::Infer`) to the actual opaque
236+
/// type (`ty::Opaque`). Used during fallback to map unconstrained
237+
/// opaque type inference variables to their corresponding
238+
/// opaque type.
239+
opaque_types_vars: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,
240+
234241
/// Each type parameter has an implicit region bound that
235242
/// indicates it must outlive at least the function body (the user
236243
/// may specify stronger requirements). This field indicates the
@@ -696,6 +703,7 @@ impl Inherited<'a, 'tcx> {
696703
deferred_cast_checks: RefCell::new(Vec::new()),
697704
deferred_generator_interiors: RefCell::new(Vec::new()),
698705
opaque_types: RefCell::new(Default::default()),
706+
opaque_types_vars: RefCell::new(Default::default()),
699707
implicit_region_bound,
700708
body_id,
701709
}
@@ -937,9 +945,46 @@ fn typeck_tables_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TypeckTables<'_> {
937945
// All type checking constraints were added, try to fallback unsolved variables.
938946
fcx.select_obligations_where_possible(false, |_| {});
939947
let mut fallback_has_occurred = false;
948+
949+
// We do fallback in two passes, to try to generate
950+
// better error messages.
951+
// The first time, we do *not* replace opaque types.
940952
for ty in &fcx.unsolved_variables() {
941-
fallback_has_occurred |= fcx.fallback_if_possible(ty);
953+
fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::NoOpaque);
954+
}
955+
// We now see if we can make progress. This might
956+
// cause us to unify inference variables for opaque types,
957+
// since we may have unified some other type variables
958+
// during the first phase of fallback.
959+
// This means that we only replace inference variables with their underlying
960+
// opaque types as a last resort.
961+
//
962+
// In code like this:
963+
//
964+
// ```rust
965+
// type MyType = impl Copy;
966+
// fn produce() -> MyType { true }
967+
// fn bad_produce() -> MyType { panic!() }
968+
// ```
969+
//
970+
// we want to unify the opaque inference variable in `bad_produce`
971+
// with the diverging fallback for `panic!` (e.g. `()` or `!`).
972+
// This will produce a nice error message about conflicting concrete
973+
// types for `MyType`.
974+
//
975+
// If we had tried to fallback the opaque inference variable to `MyType`,
976+
// we will generate a confusing type-check error that does not explicitly
977+
// refer to opaque types.
978+
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
979+
980+
// We now run fallback again, but this time we allow it to replace
981+
// unconstrained opaque type variables, in addition to performing
982+
// other kinds of fallback.
983+
for ty in &fcx.unsolved_variables() {
984+
fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::All);
942985
}
986+
987+
// See if we can make any more progress.
943988
fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
944989

945990
// Even though coercion casts provide type hints, we check casts after fallback for
@@ -2499,6 +2544,16 @@ enum TupleArgumentsFlag {
24992544
TupleArguments,
25002545
}
25012546

2547+
/// Controls how we perform fallback for unconstrained
2548+
/// type variables.
2549+
enum FallbackMode {
2550+
/// Do not fallback type variables to opaque types.
2551+
NoOpaque,
2552+
/// Perform all possible kinds of fallback, including
2553+
/// turning type variables to opaque types.
2554+
All,
2555+
}
2556+
25022557
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25032558
pub fn new(
25042559
inh: &'a Inherited<'a, 'tcx>,
@@ -2864,8 +2919,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28642919
);
28652920

28662921
let mut opaque_types = self.opaque_types.borrow_mut();
2922+
let mut opaque_types_vars = self.opaque_types_vars.borrow_mut();
28672923
for (ty, decl) in opaque_type_map {
28682924
let _ = opaque_types.insert(ty, decl);
2925+
let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
28692926
}
28702927

28712928
value
@@ -3078,7 +3135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30783135
// Fallback becomes very dubious if we have encountered type-checking errors.
30793136
// In that case, fallback to Error.
30803137
// The return value indicates whether fallback has occurred.
3081-
fn fallback_if_possible(&self, ty: Ty<'tcx>) -> bool {
3138+
fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
30823139
use rustc::ty::error::UnconstrainedNumeric::Neither;
30833140
use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
30843141

@@ -3088,7 +3145,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30883145
UnconstrainedInt => self.tcx.types.i32,
30893146
UnconstrainedFloat => self.tcx.types.f64,
30903147
Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
3091-
Neither => return false,
3148+
Neither => {
3149+
// This type variable was created from the instantiation of an opaque
3150+
// type. The fact that we're attempting to perform fallback for it
3151+
// means that the function neither constrained it to a concrete
3152+
// type, nor to the opaque type itself.
3153+
//
3154+
// For example, in this code:
3155+
//
3156+
//```
3157+
// type MyType = impl Copy;
3158+
// fn defining_use() -> MyType { true }
3159+
// fn other_use() -> MyType { defining_use() }
3160+
// ```
3161+
//
3162+
// `defining_use` will constrain the instantiated inference
3163+
// variable to `bool`, while `other_use` will constrain
3164+
// the instantiated inference variable to `MyType`.
3165+
//
3166+
// When we process opaque types during writeback, we
3167+
// will handle cases like `other_use`, and not count
3168+
// them as defining usages
3169+
//
3170+
// However, we also need to handle cases like this:
3171+
//
3172+
// ```rust
3173+
// pub type Foo = impl Copy;
3174+
// fn produce() -> Option<Foo> {
3175+
// None
3176+
// }
3177+
// ```
3178+
//
3179+
// In the above snippet, the inference varaible created by
3180+
// instantiating `Option<Foo>` will be completely unconstrained.
3181+
// We treat this as a non-defining use by making the inference
3182+
// variable fall back to the opaque type itself.
3183+
if let FallbackMode::All = mode {
3184+
if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) {
3185+
debug!("fallback_if_possible: falling back opaque type var {:?} to {:?}",
3186+
ty, opaque_ty);
3187+
*opaque_ty
3188+
} else {
3189+
return false;
3190+
}
3191+
} else {
3192+
return false;
3193+
}
3194+
},
30923195
};
30933196
debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
30943197
self.demand_eqtype(syntax_pos::DUMMY_SP, ty, fallback);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! Ideally, these tests would go in `where-allowed.rs`, but we bail out
2+
//! too early to display them.
3+
use std::fmt::Debug;
4+
5+
// Disallowed
6+
fn in_adt_in_return() -> Vec<impl Debug> { panic!() }
7+
//~^ ERROR opaque type expands to a recursive type
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0720]: opaque type expands to a recursive type
2+
--> $DIR/where-allowed-2.rs:6:30
3+
|
4+
LL | fn in_adt_in_return() -> Vec<impl Debug> { panic!() }
5+
| ^^^^^^^^^^ expands to a recursive type
6+
|
7+
= note: type resolves to itself
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0720`.

src/test/ui/impl-trait/where-allowed.rs

-5
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ fn in_return() -> impl Debug { panic!() }
1111
// Allowed
1212
fn in_adt_in_parameters(_: Vec<impl Debug>) { panic!() }
1313

14-
// Disallowed
15-
fn in_adt_in_return() -> Vec<impl Debug> { panic!() }
16-
//~^ ERROR type annotations needed
17-
1814
// Disallowed
1915
fn in_fn_parameter_in_parameters(_: fn(impl Debug)) { panic!() }
2016
//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types
@@ -60,7 +56,6 @@ fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() }
6056
fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() }
6157
//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types
6258
//~| ERROR nested `impl Trait` is not allowed
63-
//~| ERROR type annotations needed
6459

6560
// Disallowed
6661
fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { panic!() }

0 commit comments

Comments
 (0)