Rollup merge of #87200 - oli-obk:fixup_fixup_opaque_types, r=nikomatsakis
TAIT: Infer all inference variables in opaque type substitutions 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. cc `@spastorino` r? `@nikomatsakis`
This commit is contained in:
commit
7d36d69b4a
@ -857,7 +857,7 @@ pub fn is_empty(&self) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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>,
|
||||||
|
@ -568,6 +568,7 @@ fn generate_member_constraint(
|
|||||||
/// - `substs`, the substs used to instantiate this opaque type
|
/// - `substs`, the substs used to instantiate this opaque type
|
||||||
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
|
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
|
||||||
/// `opaque_defn.concrete_ty`
|
/// `opaque_defn.concrete_ty`
|
||||||
|
#[instrument(skip(self))]
|
||||||
fn infer_opaque_definition_from_instantiation(
|
fn infer_opaque_definition_from_instantiation(
|
||||||
&self,
|
&self,
|
||||||
opaque_type_key: OpaqueTypeKey<'tcx>,
|
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||||
@ -576,11 +577,6 @@ fn infer_opaque_definition_from_instantiation(
|
|||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
|
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
|
||||||
|
|
||||||
debug!(
|
|
||||||
"infer_opaque_definition_from_instantiation(def_id={:?}, instantiated_ty={:?})",
|
|
||||||
def_id, instantiated_ty
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use substs to build up a reverse map from regions to their
|
// Use substs to build up a reverse map from regions to their
|
||||||
// identity mappings. This is necessary because of `impl
|
// identity mappings. This is necessary because of `impl
|
||||||
// Trait` lifetimes are computed by replacing existing
|
// Trait` lifetimes are computed by replacing existing
|
||||||
@ -588,6 +584,7 @@ fn infer_opaque_definition_from_instantiation(
|
|||||||
// `impl Trait` return type, resulting in the parameters
|
// `impl Trait` return type, resulting in the parameters
|
||||||
// shifting.
|
// shifting.
|
||||||
let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id);
|
let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id);
|
||||||
|
debug!(?id_substs);
|
||||||
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
|
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
|
||||||
substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
|
substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
|
||||||
|
|
||||||
@ -602,7 +599,7 @@ fn infer_opaque_definition_from_instantiation(
|
|||||||
instantiated_ty,
|
instantiated_ty,
|
||||||
span,
|
span,
|
||||||
));
|
));
|
||||||
debug!("infer_opaque_definition_from_instantiation: definition_ty={:?}", definition_ty);
|
debug!(?definition_ty);
|
||||||
|
|
||||||
definition_ty
|
definition_ty
|
||||||
}
|
}
|
||||||
@ -857,7 +854,7 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
|||||||
self.tcx.mk_generator(def_id, substs, movability)
|
self.tcx.mk_generator(def_id, substs, movability)
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Param(..) => {
|
ty::Param(param) => {
|
||||||
// Look it up in the substitution list.
|
// Look it up in the substitution list.
|
||||||
match self.map.get(&ty.into()).map(|k| k.unpack()) {
|
match self.map.get(&ty.into()).map(|k| k.unpack()) {
|
||||||
// Found it in the substitution list; replace with the parameter from the
|
// Found it in the substitution list; replace with the parameter from the
|
||||||
@ -865,6 +862,7 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
|||||||
Some(GenericArgKind::Type(t1)) => t1,
|
Some(GenericArgKind::Type(t1)) => t1,
|
||||||
Some(u) => panic!("type mapped to unexpected kind: {:?}", u),
|
Some(u) => panic!("type mapped to unexpected kind: {:?}", u),
|
||||||
None => {
|
None => {
|
||||||
|
debug!(?param, ?self.map);
|
||||||
self.tcx
|
self.tcx
|
||||||
.sess
|
.sess
|
||||||
.struct_span_err(
|
.struct_span_err(
|
||||||
@ -931,8 +929,8 @@ struct Instantiator<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Instantiator<'a, 'tcx> {
|
impl<'a, 'tcx> Instantiator<'a, 'tcx> {
|
||||||
|
#[instrument(skip(self))]
|
||||||
fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
|
fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
|
||||||
debug!("instantiate_opaque_types_in_map(value={:?})", value);
|
|
||||||
let tcx = self.infcx.tcx;
|
let tcx = self.infcx.tcx;
|
||||||
value.fold_with(&mut BottomUpFolder {
|
value.fold_with(&mut BottomUpFolder {
|
||||||
tcx,
|
tcx,
|
||||||
|
@ -112,11 +112,9 @@
|
|||||||
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 {
|
||||||
|
@ -496,6 +496,8 @@ fn visit_opaque_types(&mut self, span: Span) {
|
|||||||
|
|
||||||
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 @@ fn visit_opaque_types(&mut self, span: Span) {
|
|||||||
// 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,7 +533,10 @@ fn visit_opaque_types(&mut self, span: Span) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opaque_type_key.substs.needs_infer() {
|
if opaque_type_key.substs.needs_infer() {
|
||||||
|
span_bug!(span, "{:#?} has inference variables", opaque_type_key.substs)
|
||||||
|
}
|
||||||
|
|
||||||
// We only want to add an entry into `concrete_opaque_types`
|
// We only want to add an entry into `concrete_opaque_types`
|
||||||
// if we actually found a defining usage of this opaque type.
|
// if we actually found a defining usage of this opaque type.
|
||||||
// Otherwise, we do nothing - we'll either find a defining usage
|
// Otherwise, we do nothing - we'll either find a defining usage
|
||||||
@ -554,9 +561,6 @@ fn visit_opaque_types(&mut self, span: Span) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.tcx().sess.delay_span_bug(span, "`opaque_defn` has inference variables");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user