Infer all inference variables via InferCx

The previous algorithm was correct for the example given in its
documentation, but when the TAIT was declared as a free item
instead of an associated item, the generic parameters were the
wrong ones.
This commit is contained in:
Oli Scherer 2021-07-16 17:34:23 +00:00
parent 24a8d3bce3
commit ebe21ac23a
5 changed files with 36 additions and 144 deletions

View file

@ -856,7 +856,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
} }
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable, TypeFoldable)]
pub struct OpaqueTypeKey<'tcx> { pub struct OpaqueTypeKey<'tcx> {
pub def_id: DefId, pub def_id: DefId,
pub substs: SubstsRef<'tcx>, pub substs: SubstsRef<'tcx>,

View file

@ -112,11 +112,9 @@ use rustc_hir::{HirIdMap, ImplicitSelfKind, Node};
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_index::vec::Idx; use rustc_index::vec::Idx;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::{self, RegionKind, Ty, TyCtxt, UserType}; use rustc_middle::ty::{self, Ty, TyCtxt, UserType};
use rustc_session::config; use rustc_session::config;
use rustc_session::parse::feature_err; use rustc_session::parse::feature_err;
use rustc_session::Session; use rustc_session::Session;
@ -321,117 +319,6 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet<LocalDe
&*tcx.typeck(def_id).used_trait_imports &*tcx.typeck(def_id).used_trait_imports
} }
/// Inspects the substs of opaque types, replacing any inference variables
/// with proper generic parameter from the identity substs.
///
/// This is run after we normalize the function signature, to fix any inference
/// variables introduced by the projection of associated types. This ensures that
/// any opaque types used in the signature continue to refer to generic parameters,
/// allowing them to be considered for defining uses in the function body
///
/// For example, consider this code.
///
/// ```rust
/// trait MyTrait {
/// type MyItem;
/// fn use_it(self) -> Self::MyItem
/// }
/// impl<T, I> MyTrait for T where T: Iterator<Item = I> {
/// type MyItem = impl Iterator<Item = I>;
/// fn use_it(self) -> Self::MyItem {
/// self
/// }
/// }
/// ```
///
/// When we normalize the signature of `use_it` from the impl block,
/// we will normalize `Self::MyItem` to the opaque type `impl Iterator<Item = I>`
/// However, this projection result may contain inference variables, due
/// to the way that projection works. We didn't have any inference variables
/// in the signature to begin with - leaving them in will cause us to incorrectly
/// conclude that we don't have a defining use of `MyItem`. By mapping inference
/// variables back to the actual generic parameters, we will correctly see that
/// we have a defining use of `MyItem`
fn fixup_opaque_types<'tcx, T>(tcx: TyCtxt<'tcx>, val: T) -> T
where
T: TypeFoldable<'tcx>,
{
struct FixupFolder<'tcx> {
tcx: TyCtxt<'tcx>,
}
impl<'tcx> TypeFolder<'tcx> for FixupFolder<'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
match *ty.kind() {
ty::Opaque(def_id, substs) => {
debug!("fixup_opaque_types: found type {:?}", ty);
// Here, we replace any inference variables that occur within
// the substs of an opaque type. By definition, any type occurring
// in the substs has a corresponding generic parameter, which is what
// we replace it with.
// This replacement is only run on the function signature, so any
// inference variables that we come across must be the rust of projection
// (there's no other way for a user to get inference variables into
// a function signature).
if ty.needs_infer() {
let new_substs = InternalSubsts::for_item(self.tcx, def_id, |param, _| {
let old_param = substs[param.index as usize];
match old_param.unpack() {
GenericArgKind::Type(old_ty) => {
if let ty::Infer(_) = old_ty.kind() {
// Replace inference type with a generic parameter
self.tcx.mk_param_from_def(param)
} else {
old_param.fold_with(self)
}
}
GenericArgKind::Const(old_const) => {
if let ty::ConstKind::Infer(_) = old_const.val {
// This should never happen - we currently do not support
// 'const projections', e.g.:
// `impl<T: SomeTrait> MyTrait for T where <T as SomeTrait>::MyConst == 25`
// which should be the only way for us to end up with a const inference
// variable after projection. If Rust ever gains support for this kind
// of projection, this should *probably* be changed to
// `self.tcx.mk_param_from_def(param)`
bug!(
"Found infer const: `{:?}` in opaque type: {:?}",
old_const,
ty
);
} else {
old_param.fold_with(self)
}
}
GenericArgKind::Lifetime(old_region) => {
if let RegionKind::ReVar(_) = old_region {
self.tcx.mk_param_from_def(param)
} else {
old_param.fold_with(self)
}
}
}
});
let new_ty = self.tcx.mk_opaque(def_id, new_substs);
debug!("fixup_opaque_types: new type: {:?}", new_ty);
new_ty
} else {
ty
}
}
_ => ty.super_fold_with(self),
}
}
}
debug!("fixup_opaque_types({:?})", val);
val.fold_with(&mut FixupFolder { tcx })
}
fn typeck_const_arg<'tcx>( fn typeck_const_arg<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
(did, param_did): (LocalDefId, DefId), (did, param_did): (LocalDefId, DefId),
@ -510,8 +397,6 @@ fn typeck_with_fallback<'tcx>(
fn_sig, fn_sig,
); );
let fn_sig = fixup_opaque_types(tcx, fn_sig);
let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None).0; let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None).0;
fcx fcx
} else { } else {

View file

@ -496,6 +496,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
debug_assert!(!instantiated_ty.has_escaping_bound_vars()); debug_assert!(!instantiated_ty.has_escaping_bound_vars());
let opaque_type_key = self.fcx.fully_resolve(opaque_type_key).unwrap();
// Prevent: // Prevent:
// * `fn foo<T>() -> Foo<T>` // * `fn foo<T>() -> Foo<T>`
// * `fn foo<T: Bound + Other>() -> Foo<T>` // * `fn foo<T: Bound + Other>() -> Foo<T>`
@ -508,6 +510,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
// fn foo<U>() -> Foo<U> { .. } // fn foo<U>() -> Foo<U> { .. }
// ``` // ```
// figures out the concrete type with `U`, but the stored type is with `T`. // figures out the concrete type with `U`, but the stored type is with `T`.
// FIXME: why are we calling this here? This seems too early, and duplicated.
let definition_ty = self.fcx.infer_opaque_definition_from_instantiation( let definition_ty = self.fcx.infer_opaque_definition_from_instantiation(
opaque_type_key, opaque_type_key,
instantiated_ty, instantiated_ty,
@ -529,33 +533,33 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
} }
} }
if !opaque_type_key.substs.needs_infer() { if opaque_type_key.substs.needs_infer() {
// We only want to add an entry into `concrete_opaque_types` span_bug!(span, "{:#?} has inference variables", opaque_type_key.substs)
// if we actually found a defining usage of this opaque type. }
// Otherwise, we do nothing - we'll either find a defining usage
// in some other location, or we'll end up emitting an error due // We only want to add an entry into `concrete_opaque_types`
// to the lack of defining usage // if we actually found a defining usage of this opaque type.
if !skip_add { // Otherwise, we do nothing - we'll either find a defining usage
let old_concrete_ty = self // in some other location, or we'll end up emitting an error due
.typeck_results // to the lack of defining usage
.concrete_opaque_types if !skip_add {
.insert(opaque_type_key, definition_ty); let old_concrete_ty = self
if let Some(old_concrete_ty) = old_concrete_ty { .typeck_results
if old_concrete_ty != definition_ty { .concrete_opaque_types
span_bug!( .insert(opaque_type_key, definition_ty);
span, if let Some(old_concrete_ty) = old_concrete_ty {
"`visit_opaque_types` tried to write different types for the same \ if old_concrete_ty != definition_ty {
span_bug!(
span,
"`visit_opaque_types` tried to write different types for the same \
opaque type: {:?}, {:?}, {:?}, {:?}", opaque type: {:?}, {:?}, {:?}, {:?}",
opaque_type_key.def_id, opaque_type_key.def_id,
definition_ty, definition_ty,
opaque_defn, opaque_defn,
old_concrete_ty, old_concrete_ty,
); );
}
} }
} }
} else {
self.tcx().sess.delay_span_bug(span, "`opaque_defn` has inference variables");
} }
} }
} }

View file

@ -9,7 +9,7 @@ trait Bug {
impl Bug for &() { impl Bug for &() {
type Item = impl Bug; //~ ERROR `impl Trait` in type aliases is unstable type Item = impl Bug; //~ ERROR `impl Trait` in type aliases is unstable
//~^ ERROR the trait bound `(): Bug` is not satisfied //~^ ERROR the trait bound `(): Bug` is not satisfied
//~^^ ERROR could not find defining uses //~^^ ERROR the trait bound `(): Bug` is not satisfied
const FUN: fn() -> Self::Item = || (); const FUN: fn() -> Self::Item = || ();
//~^ ERROR type alias impl trait is not permitted here //~^ ERROR type alias impl trait is not permitted here

View file

@ -25,11 +25,14 @@ LL | type Item = impl Bug;
= help: the following implementations were found: = help: the following implementations were found:
<&() as Bug> <&() as Bug>
error: could not find defining uses error[E0277]: the trait bound `(): Bug` is not satisfied
--> $DIR/issue-60371.rs:10:17 --> $DIR/issue-60371.rs:10:17
| |
LL | type Item = impl Bug; LL | type Item = impl Bug;
| ^^^^^^^^ | ^^^^^^^^ the trait `Bug` is not implemented for `()`
|
= help: the following implementations were found:
<&() as Bug>
error: aborting due to 4 previous errors error: aborting due to 4 previous errors