Stop using identity args for opaque type wf checks and instead load the args from the single use of a RPIT in its parent function's return type
This commit is contained in:
parent
420ee167a8
commit
67703b9161
2 changed files with 134 additions and 78 deletions
|
@ -13,7 +13,7 @@ use rustc_hir::intravisit::Visitor;
|
||||||
use rustc_hir::{ItemKind, Node, PathSegment};
|
use rustc_hir::{ItemKind, Node, PathSegment};
|
||||||
use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
|
use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
|
||||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||||
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
|
use rustc_infer::infer::{LateBoundRegionConversionTime, RegionVariableOrigin, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::{Obligation, TraitEngineExt as _};
|
use rustc_infer::traits::{Obligation, TraitEngineExt as _};
|
||||||
use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
|
use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
|
@ -407,7 +407,38 @@ fn check_opaque_meets_bounds<'tcx>(
|
||||||
.build();
|
.build();
|
||||||
let ocx = ObligationCtxt::new(&infcx);
|
let ocx = ObligationCtxt::new(&infcx);
|
||||||
|
|
||||||
let args = GenericArgs::identity_for_item(tcx, def_id.to_def_id());
|
let mut args = GenericArgs::identity_for_item(tcx, def_id.to_def_id());
|
||||||
|
assert!(!args.has_escaping_bound_vars(), "{args:#?}");
|
||||||
|
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
|
||||||
|
// Find use of the RPIT in the function signature and thus find the right args to
|
||||||
|
// convert it into the parameter space of the function signature. This is needed,
|
||||||
|
// because that's what `type_of` returns, against which we compare later.
|
||||||
|
let ret = tcx.fn_sig(defining_use_anchor).instantiate_identity().output();
|
||||||
|
|
||||||
|
let a = ret
|
||||||
|
.skip_binder()
|
||||||
|
.visit_with(&mut FindOpaqueTypeArgs {
|
||||||
|
tcx,
|
||||||
|
opaque: def_id.to_def_id(),
|
||||||
|
fn_def_id: defining_use_anchor.to_def_id(),
|
||||||
|
seen: Default::default(),
|
||||||
|
depth: ty::INNERMOST,
|
||||||
|
})
|
||||||
|
.break_value()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
tcx.sess.delay_span_bug(
|
||||||
|
tcx.def_span(defining_use_anchor),
|
||||||
|
format!("return type of {defining_use_anchor:?} does not contain {def_id:?}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let a = infcx.instantiate_binder_with_fresh_vars(
|
||||||
|
span,
|
||||||
|
LateBoundRegionConversionTime::HigherRankedType,
|
||||||
|
ret.rebind(a),
|
||||||
|
);
|
||||||
|
assert!(!a.has_escaping_bound_vars(), "{a:#?}");
|
||||||
|
args = ty::EarlyBinder::bind(args).instantiate(tcx, a);
|
||||||
|
}
|
||||||
let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
|
let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);
|
||||||
|
|
||||||
// `ReErased` regions appear in the "parent_args" of closures/generators.
|
// `ReErased` regions appear in the "parent_args" of closures/generators.
|
||||||
|
@ -468,9 +499,10 @@ fn check_opaque_meets_bounds<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
|
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
|
||||||
for (key, mut ty) in infcx.take_opaque_types() {
|
for (mut key, mut ty) in infcx.take_opaque_types() {
|
||||||
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
|
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
|
||||||
sanity_check_found_hidden_type(tcx, key, ty.hidden_type, defining_use_anchor, origin)?;
|
key = infcx.resolve_vars_if_possible(key);
|
||||||
|
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -479,8 +511,6 @@ fn sanity_check_found_hidden_type<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
key: ty::OpaqueTypeKey<'tcx>,
|
key: ty::OpaqueTypeKey<'tcx>,
|
||||||
mut ty: ty::OpaqueHiddenType<'tcx>,
|
mut ty: ty::OpaqueHiddenType<'tcx>,
|
||||||
defining_use_anchor: LocalDefId,
|
|
||||||
origin: &hir::OpaqueTyOrigin,
|
|
||||||
) -> Result<(), ErrorGuaranteed> {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
if ty.ty.is_ty_var() {
|
if ty.ty.is_ty_var() {
|
||||||
// Nothing was actually constrained.
|
// Nothing was actually constrained.
|
||||||
|
@ -493,29 +523,23 @@ fn sanity_check_found_hidden_type<'tcx>(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let strip_vars = |ty: Ty<'tcx>| {
|
||||||
|
ty.fold_with(&mut BottomUpFolder {
|
||||||
|
tcx,
|
||||||
|
ty_op: |t| t,
|
||||||
|
ct_op: |c| c,
|
||||||
|
lt_op: |l| match l.kind() {
|
||||||
|
RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
|
||||||
|
_ => l,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
};
|
||||||
// Closures frequently end up containing erased lifetimes in their final representation.
|
// Closures frequently end up containing erased lifetimes in their final representation.
|
||||||
// These correspond to lifetime variables that never got resolved, so we patch this up here.
|
// These correspond to lifetime variables that never got resolved, so we patch this up here.
|
||||||
ty.ty = ty.ty.fold_with(&mut BottomUpFolder {
|
ty.ty = strip_vars(ty.ty);
|
||||||
tcx,
|
|
||||||
ty_op: |t| t,
|
|
||||||
ct_op: |c| c,
|
|
||||||
lt_op: |l| match l.kind() {
|
|
||||||
RegionKind::ReVar(_) => tcx.lifetimes.re_erased,
|
|
||||||
_ => l,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// Get the hidden type.
|
// Get the hidden type.
|
||||||
let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
|
let hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
|
||||||
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
|
let hidden_ty = strip_vars(hidden_ty);
|
||||||
if hidden_ty != ty.ty {
|
|
||||||
hidden_ty = find_and_apply_rpit_args(
|
|
||||||
tcx,
|
|
||||||
hidden_ty,
|
|
||||||
defining_use_anchor.to_def_id(),
|
|
||||||
key.def_id.to_def_id(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the hidden types differ, emit a type mismatch diagnostic.
|
// If the hidden types differ, emit a type mismatch diagnostic.
|
||||||
if hidden_ty == ty.ty {
|
if hidden_ty == ty.ty {
|
||||||
|
@ -563,67 +587,80 @@ fn sanity_check_found_hidden_type<'tcx>(
|
||||||
/// x
|
/// x
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn find_and_apply_rpit_args<'tcx>(
|
struct FindOpaqueTypeArgs<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
mut hidden_ty: Ty<'tcx>,
|
|
||||||
function: DefId,
|
|
||||||
opaque: DefId,
|
opaque: DefId,
|
||||||
) -> Result<Ty<'tcx>, ErrorGuaranteed> {
|
seen: FxHashSet<DefId>,
|
||||||
// Find use of the RPIT in the function signature and thus find the right args to
|
fn_def_id: DefId,
|
||||||
// convert it into the parameter space of the function signature. This is needed,
|
depth: ty::DebruijnIndex,
|
||||||
// because that's what `type_of` returns, against which we compare later.
|
}
|
||||||
let ret = tcx.fn_sig(function).instantiate_identity().output();
|
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FindOpaqueTypeArgs<'tcx> {
|
||||||
struct Visitor<'tcx> {
|
type BreakTy = GenericArgsRef<'tcx>;
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
opaque: DefId,
|
|
||||||
seen: FxHashSet<DefId>,
|
|
||||||
}
|
|
||||||
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> {
|
|
||||||
type BreakTy = GenericArgsRef<'tcx>;
|
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self), ret)]
|
#[instrument(level = "trace", skip(self), ret)]
|
||||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||||
trace!("{:#?}", t.kind());
|
&mut self,
|
||||||
match t.kind() {
|
t: &ty::Binder<'tcx, T>,
|
||||||
ty::Alias(ty::Opaque, alias) => {
|
) -> ControlFlow<Self::BreakTy> {
|
||||||
trace!(?alias.def_id);
|
self.depth.shift_in(1);
|
||||||
if alias.def_id == self.opaque {
|
let binder = t.super_visit_with(self);
|
||||||
return ControlFlow::Break(alias.args);
|
self.depth.shift_out(1);
|
||||||
} else if self.seen.insert(alias.def_id) {
|
binder
|
||||||
for clause in self
|
}
|
||||||
.tcx
|
|
||||||
.explicit_item_bounds(alias.def_id)
|
#[instrument(level = "trace", skip(self), ret)]
|
||||||
.iter_instantiated_copied(self.tcx, alias.args)
|
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
{
|
trace!("{:#?}", t.kind());
|
||||||
trace!(?clause);
|
match t.kind() {
|
||||||
clause.visit_with(self)?;
|
ty::Alias(ty::Opaque, alias) => {
|
||||||
|
trace!(?alias.def_id);
|
||||||
|
if alias.def_id == self.opaque {
|
||||||
|
let args = self.tcx.fold_regions(alias.args, |re, depth| {
|
||||||
|
if let ty::ReLateBound(index, bv) = re.kind() {
|
||||||
|
if depth != ty::INNERMOST {
|
||||||
|
return ty::Region::new_error_with_message(
|
||||||
|
self.tcx,
|
||||||
|
self.tcx.def_span(self.opaque),
|
||||||
|
"opaque type behind meaningful binders are not supported yet",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ty::Region::new_late_bound(
|
||||||
|
self.tcx,
|
||||||
|
index.shifted_out_to_binder(self.depth),
|
||||||
|
bv,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
re
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
return ControlFlow::Break(args);
|
||||||
|
} else if self.seen.insert(alias.def_id) {
|
||||||
|
for clause in self
|
||||||
|
.tcx
|
||||||
|
.explicit_item_bounds(alias.def_id)
|
||||||
|
.iter_instantiated_copied(self.tcx, alias.args)
|
||||||
|
{
|
||||||
|
trace!(?clause);
|
||||||
|
clause.visit_with(self)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty::Alias(ty::Weak, alias) => {
|
|
||||||
self.tcx
|
|
||||||
.type_of(alias.def_id)
|
|
||||||
.instantiate(self.tcx, alias.args)
|
|
||||||
.visit_with(self)?;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
ty::Alias(ty::Projection, alias) => {
|
||||||
t.super_visit_with(self)
|
if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) = self.tcx.opt_rpitit_info(alias.def_id) && fn_def_id == self.fn_def_id {
|
||||||
|
self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).visit_with(self)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::Alias(ty::Weak, alias) => {
|
||||||
|
self.tcx
|
||||||
|
.type_of(alias.def_id)
|
||||||
|
.instantiate(self.tcx, alias.args)
|
||||||
|
.visit_with(self)?;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.super_visit_with(self)
|
||||||
}
|
}
|
||||||
if let ControlFlow::Break(args) =
|
|
||||||
ret.visit_with(&mut Visitor { tcx, opaque, seen: Default::default() })
|
|
||||||
{
|
|
||||||
trace!(?args);
|
|
||||||
trace!("expected: {hidden_ty:#?}");
|
|
||||||
hidden_ty = ty::EarlyBinder::bind(hidden_ty).instantiate(tcx, args);
|
|
||||||
trace!("expected: {hidden_ty:#?}");
|
|
||||||
} else {
|
|
||||||
tcx.sess
|
|
||||||
.delay_span_bug(tcx.def_span(function), format!("{ret:?} does not contain {opaque:?}"));
|
|
||||||
}
|
|
||||||
Ok(hidden_ty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_enum_of_nonnullable_ptr<'tcx>(
|
fn is_enum_of_nonnullable_ptr<'tcx>(
|
||||||
|
|
19
tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs
Normal file
19
tests/ui/type-alias-impl-trait/wf-check-rpit-lifetimes.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//check-pass
|
||||||
|
|
||||||
|
pub struct Key;
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Value;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct DiagnosticBuilder<'db> {
|
||||||
|
inner: HashMap<&'db Key, Vec<&'db Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> DiagnosticBuilder<'db> {
|
||||||
|
pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'db Key, impl Iterator<Item = &'a Value>)> {
|
||||||
|
self.inner.iter().map(|(key, values)| (*key, values.iter().map(|v| *v)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue