rework opaque types region inference
This commit is contained in:
parent
08c8caa524
commit
f4940e4d22
@ -1,10 +1,10 @@
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::OpaqueTyOrigin;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
|
||||
use rustc_infer::traits::{Obligation, ObligationCause};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::traits::DefiningAnchor;
|
||||
@ -95,8 +95,8 @@ fn check_unique(
|
||||
/// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
|
||||
/// This is lowered to give HIR something like
|
||||
///
|
||||
/// type f<'a>::_Return<'_a> = impl Sized + '_a;
|
||||
/// fn f<'a>(x: &'a i32) -> f<'static>::_Return<'a> { x }
|
||||
/// type f<'a>::_Return<'_x> = impl Sized + '_x;
|
||||
/// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x }
|
||||
///
|
||||
/// When checking the return type record the type from the return and the
|
||||
/// type used in the return value. In this case they might be `_Return<'1>`
|
||||
@ -104,30 +104,34 @@ fn check_unique(
|
||||
///
|
||||
/// Once we to this method, we have completed region inference and want to
|
||||
/// call `infer_opaque_definition_from_instantiation` to get the inferred
|
||||
/// type of `_Return<'_a>`. `infer_opaque_definition_from_instantiation`
|
||||
/// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation`
|
||||
/// compares lifetimes directly, so we need to map the inference variables
|
||||
/// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
|
||||
///
|
||||
/// First we map all the lifetimes in the concrete type to an equal
|
||||
/// universal region that occurs in the concrete type's args, in this case
|
||||
/// this would result in `&'1 i32`. We only consider regions in the args
|
||||
/// First we map the regions in the the generic parameters `_Return<'1>` to
|
||||
/// their `external_name` giving `_Return<'a>`. This step is a bit involved.
|
||||
/// See the [rustc-dev-guide chapter] for more info.
|
||||
///
|
||||
/// Then we map all the lifetimes in the concrete type to an equal
|
||||
/// universal region that occurs in the opaque type's args, in this case
|
||||
/// this would result in `&'a i32`. We only consider regions in the args
|
||||
/// in case there is an equal region that does not. For example, this should
|
||||
/// be allowed:
|
||||
/// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
|
||||
///
|
||||
/// Then we map the regions in both the type and the generic parameters to their
|
||||
/// `external_name` giving `concrete_type = &'a i32`,
|
||||
/// `args = ['static, 'a]`. This will then allow
|
||||
/// `infer_opaque_definition_from_instantiation` to determine that
|
||||
/// `_Return<'_a> = &'_a i32`.
|
||||
/// This will then allow `infer_opaque_definition_from_instantiation` to
|
||||
/// determine that `_Return<'_x> = &'_x i32`.
|
||||
///
|
||||
/// There's a slight complication around closures. Given
|
||||
/// `fn f<'a: 'a>() { || {} }` the closure's type is something like
|
||||
/// `f::<'a>::{{closure}}`. The region parameter from f is essentially
|
||||
/// ignored by type checking so ends up being inferred to an empty region.
|
||||
/// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
|
||||
/// which has no `external_name` in which case we use `'empty` as the
|
||||
/// which has no `external_name` in which case we use `'{erased}` as the
|
||||
/// region to pass to `infer_opaque_definition_from_instantiation`.
|
||||
///
|
||||
/// [rustc-dev-guide chapter]:
|
||||
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
|
||||
#[instrument(level = "debug", skip(self, infcx), ret)]
|
||||
pub(crate) fn infer_opaque_types(
|
||||
&self,
|
||||
@ -138,85 +142,59 @@ pub(crate) fn infer_opaque_types(
|
||||
|
||||
let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
|
||||
|
||||
let member_constraints: FxIndexMap<_, _> = self
|
||||
.member_constraints
|
||||
.all_indices()
|
||||
.map(|ci| (self.member_constraints[ci].key, ci))
|
||||
.collect();
|
||||
debug!(?member_constraints);
|
||||
|
||||
for (opaque_type_key, concrete_type) in opaque_ty_decls {
|
||||
let args = opaque_type_key.args;
|
||||
debug!(?concrete_type, ?args);
|
||||
debug!(?opaque_type_key, ?concrete_type);
|
||||
|
||||
let mut arg_regions = vec![self.universal_regions.fr_static];
|
||||
|
||||
let to_universal_region = |vid, arg_regions: &mut Vec<_>| match self.universal_name(vid)
|
||||
{
|
||||
Some(region) => {
|
||||
let vid = self.universal_regions.to_region_vid(region);
|
||||
arg_regions.push(vid);
|
||||
region
|
||||
}
|
||||
None => {
|
||||
arg_regions.push(vid);
|
||||
ty::Region::new_error_with_message(
|
||||
infcx.tcx,
|
||||
concrete_type.span,
|
||||
"opaque type with non-universal region args",
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Start by inserting universal regions from the member_constraint choice regions.
|
||||
// This will ensure they get precedence when folding the regions in the concrete type.
|
||||
if let Some(&ci) = member_constraints.get(&opaque_type_key) {
|
||||
for &vid in self.member_constraints.choice_regions(ci) {
|
||||
to_universal_region(vid, &mut arg_regions);
|
||||
}
|
||||
}
|
||||
debug!(?arg_regions);
|
||||
|
||||
// Next, insert universal regions from args, so we can translate regions that appear
|
||||
// in them but are not subject to member constraints, for instance closure args.
|
||||
let universal_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
|
||||
if let ty::RePlaceholder(..) = region.kind() {
|
||||
// Higher kinded regions don't need remapping, they don't refer to anything outside of this the args.
|
||||
return region;
|
||||
}
|
||||
let vid = self.to_region_vid(region);
|
||||
to_universal_region(vid, &mut arg_regions)
|
||||
});
|
||||
let universal_args = universal_key.args;
|
||||
debug!(?universal_args);
|
||||
debug!(?arg_regions);
|
||||
|
||||
// Deduplicate the set of regions while keeping the chosen order.
|
||||
let arg_regions = arg_regions.into_iter().collect::<FxIndexSet<_>>();
|
||||
debug!(?arg_regions);
|
||||
|
||||
let universal_concrete_type =
|
||||
infcx.tcx.fold_regions(concrete_type, |region, _| match *region {
|
||||
ty::ReVar(vid) => arg_regions
|
||||
.iter()
|
||||
.find(|ur_vid| self.eval_equal(vid, **ur_vid))
|
||||
.and_then(|ur_vid| self.definitions[*ur_vid].external_name)
|
||||
.unwrap_or(infcx.tcx.lifetimes.re_erased),
|
||||
ty::RePlaceholder(_) => ty::Region::new_error_with_message(
|
||||
infcx.tcx,
|
||||
concrete_type.span,
|
||||
"hidden type contains placeholders, we don't support higher kinded opaques yet",
|
||||
),
|
||||
_ => region,
|
||||
});
|
||||
debug!(?universal_concrete_type);
|
||||
let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
|
||||
vec![(self.universal_regions.fr_static, infcx.tcx.lifetimes.re_static)];
|
||||
|
||||
let opaque_type_key =
|
||||
OpaqueTypeKey { def_id: opaque_type_key.def_id, args: universal_args };
|
||||
let ty = infcx.infer_opaque_definition_from_instantiation(
|
||||
opaque_type_key,
|
||||
universal_concrete_type,
|
||||
);
|
||||
opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
|
||||
// Use the SCC representative instead of directly using `region`.
|
||||
// See [rustc-dev-guide chapter] § "Strict lifetime equality".
|
||||
let scc = self.constraint_sccs.scc(region.as_var());
|
||||
let vid = self.scc_representatives[scc];
|
||||
let named = match self.definitions[vid].origin {
|
||||
// Iterate over all universal regions in a consistent order and find the
|
||||
// *first* equal region. This makes sure that equal lifetimes will have
|
||||
// the same name and simplifies subsequent handling.
|
||||
// See [rustc-dev-guide chapter] § "Semantic lifetime equality".
|
||||
NllRegionVariableOrigin::FreeRegion => self
|
||||
.universal_regions
|
||||
.universal_regions()
|
||||
.filter(|&ur| self.universal_region_relations.equal(vid, ur))
|
||||
// FIXME(aliemjay): universal regions with no `external_name`
|
||||
// are extenal closure regions, which should be rejected eventually.
|
||||
.find_map(|ur| self.definitions[ur].external_name),
|
||||
NllRegionVariableOrigin::Placeholder(placeholder) => {
|
||||
Some(ty::Region::new_placeholder(infcx.tcx, placeholder))
|
||||
}
|
||||
NllRegionVariableOrigin::Existential { .. } => None,
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
ty::Region::new_error_with_message(
|
||||
infcx.tcx,
|
||||
concrete_type.span,
|
||||
"opaque type with non-universal region args",
|
||||
)
|
||||
});
|
||||
|
||||
arg_regions.push((vid, named));
|
||||
named
|
||||
});
|
||||
debug!(?opaque_type_key, ?arg_regions);
|
||||
|
||||
let concrete_type = infcx.tcx.fold_regions(concrete_type, |region, _| {
|
||||
arg_regions
|
||||
.iter()
|
||||
.find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid))
|
||||
.map(|&(_, arg_named)| arg_named)
|
||||
.unwrap_or(infcx.tcx.lifetimes.re_erased)
|
||||
});
|
||||
debug!(?concrete_type);
|
||||
|
||||
let ty =
|
||||
infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type);
|
||||
// Sometimes two opaque types are the same only after we remap the generic parameters
|
||||
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)`
|
||||
// and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we only know that
|
||||
|
@ -164,6 +164,13 @@ pub(crate) fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
|
||||
self.outlives.contains(fr1, fr2)
|
||||
}
|
||||
|
||||
/// Returns `true` if fr1 is known to equal fr2.
|
||||
///
|
||||
/// This will only ever be true for universally quantified regions.
|
||||
pub(crate) fn equal(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
|
||||
self.outlives.contains(fr1, fr2) && self.outlives.contains(fr2, fr1)
|
||||
}
|
||||
|
||||
/// Returns a vector of free regions `x` such that `fr1: x` is
|
||||
/// known to hold.
|
||||
pub(crate) fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<RegionVid> {
|
||||
|
@ -8,12 +8,24 @@
|
||||
trait Indirect { type Ty; }
|
||||
impl<A, B: Equate<Proj = A>> Indirect for (A, B) { type Ty = (); }
|
||||
|
||||
type Opq = impl Sized;
|
||||
fn define_1(_: Opq) {
|
||||
let _ = None::<<(Opq, u8) as Indirect>::Ty>;
|
||||
mod basic {
|
||||
use super::*;
|
||||
type Opq = impl Sized;
|
||||
fn define_1(_: Opq) {
|
||||
let _ = None::<<(Opq, u8) as Indirect>::Ty>;
|
||||
}
|
||||
fn define_2() -> Opq {
|
||||
0u8
|
||||
}
|
||||
}
|
||||
fn define_2() -> Opq {
|
||||
0u8
|
||||
|
||||
// `Opq<'a> == &'b u8` shouldn't be an error because `'a == 'b`.
|
||||
mod lifetime {
|
||||
use super::*;
|
||||
type Opq<'a> = impl Sized + 'a;
|
||||
fn define<'a: 'b, 'b: 'a>(_: Opq<'a>) {
|
||||
let _ = None::<<(Opq<'a>, &'b u8) as Indirect>::Ty>;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -0,0 +1,37 @@
|
||||
// issue: #112841
|
||||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
mod mod1 {
|
||||
type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
|
||||
fn test<'a>() -> Opaque<'a, 'a> {}
|
||||
//~^ ERROR non-defining opaque type use in defining scope
|
||||
//~| ERROR non-defining opaque type use in defining scope
|
||||
}
|
||||
|
||||
mod mod2 {
|
||||
type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
|
||||
fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> {}
|
||||
//~^ ERROR non-defining opaque type use in defining scope
|
||||
}
|
||||
|
||||
mod mod3 {
|
||||
type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
|
||||
fn test<'a: 'b, 'b: 'a>(a: &'a str) -> Opaque<'a, 'b> { a }
|
||||
//~^ ERROR non-defining opaque type use in defining scope
|
||||
}
|
||||
|
||||
// This is similar to the previous cases in that 'a is equal to 'static,
|
||||
// which is is some sense an implicit parameter to `Opaque`.
|
||||
// For example, given a defining use `Opaque<'a> := &'a ()`,
|
||||
// it is ambiguous whether `Opaque<'a> := &'a ()` or `Opaque<'a> := &'static ()`
|
||||
mod mod4 {
|
||||
type Opaque<'a> = impl super::Trait<'a, 'a>;
|
||||
fn test<'a: 'static>() -> Opaque<'a> {}
|
||||
//~^ ERROR expected generic lifetime parameter, found `'static`
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,59 @@
|
||||
error: non-defining opaque type use in defining scope
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:10:22
|
||||
|
|
||||
LL | fn test<'a>() -> Opaque<'a, 'a> {}
|
||||
| ^^^^^^^^^^^^^^ generic argument `'a` used twice
|
||||
|
|
||||
note: for this opaque type
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:9:27
|
||||
|
|
||||
LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: non-defining opaque type use in defining scope
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:10:37
|
||||
|
|
||||
LL | fn test<'a>() -> Opaque<'a, 'a> {}
|
||||
| ^^
|
||||
|
|
||||
note: lifetime used multiple times
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:9:17
|
||||
|
|
||||
LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
|
||||
| ^^ ^^
|
||||
|
||||
error: non-defining opaque type use in defining scope
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:17:49
|
||||
|
|
||||
LL | fn test<'a: 'b, 'b: 'a>() -> Opaque<'a, 'b> {}
|
||||
| ^^
|
||||
|
|
||||
note: lifetime used multiple times
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:16:17
|
||||
|
|
||||
LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
|
||||
| ^^ ^^
|
||||
|
||||
error: non-defining opaque type use in defining scope
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:23:61
|
||||
|
|
||||
LL | fn test<'a: 'b, 'b: 'a>(a: &'a str) -> Opaque<'a, 'b> { a }
|
||||
| ^
|
||||
|
|
||||
note: lifetime used multiple times
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:22:17
|
||||
|
|
||||
LL | type Opaque<'a, 'b> = impl super::Trait<'a, 'b>;
|
||||
| ^^ ^^
|
||||
|
||||
error[E0792]: expected generic lifetime parameter, found `'static`
|
||||
--> $DIR/equal-lifetime-params-not-ok.rs:33:42
|
||||
|
|
||||
LL | type Opaque<'a> = impl super::Trait<'a, 'a>;
|
||||
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
|
||||
LL | fn test<'a: 'static>() -> Opaque<'a> {}
|
||||
| ^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0792`.
|
@ -0,0 +1,12 @@
|
||||
error[E0792]: expected generic lifetime parameter, found `'_`
|
||||
--> $DIR/generic-not-strictly-equal.rs:33:5
|
||||
|
|
||||
LL | type Opaque<'a> = impl Copy + Captures<'a>;
|
||||
| -- this generic parameter must be used with a generic lifetime parameter
|
||||
...
|
||||
LL | relate(opaque, hidden); // defining use: Opaque<'?1> := u8
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0792`.
|
@ -0,0 +1,15 @@
|
||||
error[E0700]: hidden type for `Opaque<'x>` captures lifetime that does not appear in bounds
|
||||
--> $DIR/generic-not-strictly-equal.rs:33:5
|
||||
|
|
||||
LL | type Opaque<'a> = impl Copy + Captures<'a>;
|
||||
| ------------------------ opaque type defined here
|
||||
LL |
|
||||
LL | fn test<'x>(_: Opaque<'x>) {
|
||||
| -- hidden type `&'x u8` captures the lifetime `'x` as defined here
|
||||
...
|
||||
LL | relate(opaque, hidden); // defining use: Opaque<'?1> := u8
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
38
tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs
Normal file
38
tests/ui/type-alias-impl-trait/generic-not-strictly-equal.rs
Normal file
@ -0,0 +1,38 @@
|
||||
// `Opaque<'?1> := u8` is not a valid defining use here.
|
||||
//
|
||||
// Due to fundamental limitations of the member constraints algorithm,
|
||||
// we require '?1 to be *equal* to some universal region.
|
||||
//
|
||||
// While '?1 is eventually inferred to be equal to 'x because of the constraint '?1: 'x,
|
||||
// we don't consider them equal in the strict sense because they lack the bidirectional outlives
|
||||
// constraints ['?1: 'x, 'x: '?1]. In NLL terms, they are not part of the same SCC.
|
||||
//
|
||||
// See #113971 for details.
|
||||
|
||||
//@ revisions: basic member_constraints
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
trait Captures<'a> {}
|
||||
impl<T> Captures<'_> for T {}
|
||||
|
||||
fn ensure_outlives<'a, X: 'a>(_: X) {}
|
||||
fn relate<X>(_: X, _: X) {}
|
||||
|
||||
type Opaque<'a> = impl Copy + Captures<'a>;
|
||||
|
||||
fn test<'x>(_: Opaque<'x>) {
|
||||
let opaque = None::<Opaque<'_>>; // let's call this lifetime '?1
|
||||
|
||||
#[cfg(basic)]
|
||||
let hidden = None::<u8>;
|
||||
|
||||
#[cfg(member_constraints)]
|
||||
let hidden = None::<&'x u8>;
|
||||
|
||||
ensure_outlives::<'x>(opaque); // outlives constraint: '?1: 'x
|
||||
relate(opaque, hidden); // defining use: Opaque<'?1> := u8
|
||||
//[basic]~^ ERROR expected generic lifetime parameter, found `'_`
|
||||
//[member_constraints]~^^ ERROR captures lifetime that does not appear in bounds
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user