Skip to content

Commit

Permalink
simplify check_unique
Browse files Browse the repository at this point in the history
  • Loading branch information
aliemjay committed Mar 28, 2024
1 parent 6b6ed2e commit 7c6876f
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 250 deletions.
95 changes: 25 additions & 70 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_macros::extension;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::RegionVid;
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{GenericArgKind, GenericArgs};
use rustc_span::Span;
Expand All @@ -23,73 +22,6 @@ use crate::universal_regions::RegionClassification;
use super::RegionInferenceContext;

impl<'tcx> RegionInferenceContext<'tcx> {
fn universal_name(&self, vid: ty::RegionVid) -> Option<ty::Region<'tcx>> {
let scc = self.constraint_sccs.scc(vid);
self.scc_values
.universal_regions_outlived_by(scc)
.find_map(|lb| self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?))
}

fn generic_arg_to_region(&self, arg: ty::GenericArg<'tcx>) -> Option<RegionVid> {
let region = arg.as_region()?;

if let ty::RePlaceholder(..) = region.kind() {
None
} else {
Some(self.to_region_vid(region))
}
}

/// Check that all opaque types have the same region parameters if they have the same
/// non-region parameters. This is necessary because within the new solver we perform various query operations
/// modulo regions, and thus could unsoundly select some impls that don't hold.
fn check_unique(
&self,
infcx: &InferCtxt<'tcx>,
opaque_ty_decls: &FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
) {
for (i, (a, a_ty)) in opaque_ty_decls.iter().enumerate() {
for (b, b_ty) in opaque_ty_decls.iter().skip(i + 1) {
if a.def_id != b.def_id {
continue;
}
// Non-lifetime params differ -> ok
if infcx.tcx.erase_regions(a.args) != infcx.tcx.erase_regions(b.args) {
continue;
}
trace!(?a, ?b);
for (a, b) in a.args.iter().zip(b.args) {
trace!(?a, ?b);
let Some(r1) = self.generic_arg_to_region(a) else {
continue;
};
let Some(r2) = self.generic_arg_to_region(b) else {
continue;
};
if self.eval_equal(r1, r2) {
continue;
}

// Ignore non-universal regions because they result in an error eventually.
// FIXME(aliemjay): This logic will be rewritten in a later commit.
let Some(r1) = self.universal_name(r1) else {
continue;
};
let Some(r2) = self.universal_name(r2) else {
continue;
};

infcx.dcx().emit_err(LifetimeMismatchOpaqueParam {
arg: r1.into(),
prev: r2.into(),
span: a_ty.span,
prev_span: b_ty.span,
});
}
}
}
}

/// Resolve any opaque types that were encountered while borrow checking
/// this item. This is then used to get the type in the `type_of` query.
///
Expand Down Expand Up @@ -139,9 +71,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
infcx: &InferCtxt<'tcx>,
opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
self.check_unique(infcx, &opaque_ty_decls);

let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
FxIndexMap::default();

for (opaque_type_key, concrete_type) in opaque_ty_decls {
debug!(?opaque_type_key, ?concrete_type);
Expand Down Expand Up @@ -228,6 +160,29 @@ impl<'tcx> RegionInferenceContext<'tcx> {
OpaqueHiddenType { ty, span: concrete_type.span },
);
}

// Check that all opaque types have the same region parameters if they have the same
// non-region parameters. This is necessary because within the new solver we perform
// various query operations modulo regions, and thus could unsoundly select some impls
// that don't hold.
if !ty.references_error()
&& let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert(
infcx.tcx.erase_regions(opaque_type_key),
(opaque_type_key, concrete_type.span),
)
&& let Some((arg1, arg2)) = std::iter::zip(
prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
)
.find(|(arg1, arg2)| arg1 != arg2)
{
infcx.dcx().emit_err(LifetimeMismatchOpaqueParam {
arg: arg1,
prev: arg2,
span: prev_span,
prev_span: concrete_type.span,
});
}
}
result
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/defining-use-captured-non-universal-region.rs:15:18
--> $DIR/defining-use-captured-non-universal-region.rs:14:18
|
LL | fn foo<'a>() -> impl Sized + 'a {
| -- this generic parameter must be used with a generic lifetime parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
fn foo<'a>() -> impl Sized + 'a {
#[cfg(statik)]
let i: i32 = foo::<'static>();
//[statik]~^ ERROR opaque type used twice with different lifetimes
//[statik]~| ERROR opaque type used twice with different lifetimes
//[statik]~^ ERROR expected generic lifetime parameter, found `'static`

#[cfg(infer)]
let i: i32 = foo::<'_>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,12 @@
error: opaque type used twice with different lifetimes
--> $DIR/defining-use-captured-non-universal-region.rs:10:18
|
LL | let i: i32 = foo::<'static>();
| ^^^^^^^^^^^^^^^^ lifetime `'static` used here
...
LL | i
| - lifetime `'a` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
--> $DIR/defining-use-captured-non-universal-region.rs:10:18
|
LL | let i: i32 = foo::<'static>();
| ^^^^^^^^^^^^^^^^

error: opaque type used twice with different lifetimes
--> $DIR/defining-use-captured-non-universal-region.rs:10:18
|
LL | let i: i32 = foo::<'static>();
| ^^^^^^^^^^^^^^^^ lifetime `'static` used here
...
LL | i
| - lifetime `'a` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
error[E0792]: expected generic lifetime parameter, found `'static`
--> $DIR/defining-use-captured-non-universal-region.rs:10:18
|
LL | fn foo<'a>() -> impl Sized + 'a {
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
LL | #[cfg(statik)]
LL | let i: i32 = foo::<'static>();
| ^^^^^^^^^^^^^^^^
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0792`.
4 changes: 2 additions & 2 deletions tests/ui/impl-trait/rpit/non-defining-use-lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod statik {
// invalid defining use: Opaque<'static> := ()
fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
let _: () = foo(Lt::<'static>::None);
//~^ ERROR opaque type used twice with different lifetimes
//~^ ERROR expected generic lifetime parameter, found `'static`
}
}

Expand All @@ -31,7 +31,7 @@ mod equal {
// because of the use of equal lifetimes in args
fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
//~^ ERROR opaque type used twice with different lifetimes
//~^ ERROR non-defining opaque type use in defining scope
}
}

Expand Down
34 changes: 10 additions & 24 deletions tests/ui/impl-trait/rpit/non-defining-use-lifetimes.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
error: opaque type used twice with different lifetimes
--> $DIR/non-defining-use-lifetimes.rs:14:16
|
LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
| ______________________________________________-
LL | | let _: () = foo(Lt::<'static>::None);
| | ^^ lifetime `'static` used here
LL | |
LL | | }
| |_____- lifetime `'a` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
error[E0792]: expected generic lifetime parameter, found `'static`
--> $DIR/non-defining-use-lifetimes.rs:14:16
|
LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
LL | let _: () = foo(Lt::<'static>::None);
| ^^

Expand All @@ -23,22 +14,17 @@ LL | fn foo<'a>(_: Lt<'a>) -> impl Sized + 'a {
LL | let _: () = foo(Lt::<'_>::None);
| ^^

error: opaque type used twice with different lifetimes
--> $DIR/non-defining-use-lifetimes.rs:33:16
|
LL | fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
| __________________________________________________________________-
LL | | let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
| | ^^ lifetime `'a` used here
LL | |
LL | | }
| |_____- lifetime `'b` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
error: non-defining opaque type use in defining scope
--> $DIR/non-defining-use-lifetimes.rs:33:16
|
LL | let _: () = foo(Lt::<'a>::None, Lt::<'a>::None);
| ^^
|
note: lifetime used multiple times
--> $DIR/non-defining-use-lifetimes.rs:32:58
|
LL | fn foo<'a, 'b>(_: Lt<'a>, _: Lt<'b>) -> impl Sized + 'a + 'b {
| ^^ ^^

error: aborting due to 3 previous errors

Expand Down
4 changes: 0 additions & 4 deletions tests/ui/type-alias-impl-trait/lifetime_mismatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@ type Foo<'a> = impl Sized;
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> (Foo<'a>, Foo<'b>) {
(x, y)
//~^ ERROR opaque type used twice with different lifetimes
//~| ERROR opaque type used twice with different lifetimes
}

type Bar<'a, 'b> = impl std::fmt::Debug;

fn bar<'x, 'y>(i: &'x i32, j: &'y i32) -> (Bar<'x, 'y>, Bar<'y, 'x>) {
(i, j)
//~^ ERROR opaque type used twice with different lifetimes
//~| ERROR opaque type used twice with different lifetimes
//~| ERROR opaque type used twice with different lifetimes
//~| ERROR opaque type used twice with different lifetimes
}

fn main() {
Expand Down
69 changes: 3 additions & 66 deletions tests/ui/type-alias-impl-trait/lifetime_mismatch.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,7 @@ LL | (x, y)
| ^^^^^^

error: opaque type used twice with different lifetimes
--> $DIR/lifetime_mismatch.rs:6:5
|
LL | (x, y)
| ^^^^^^
| |
| lifetime `'a` used here
| lifetime `'b` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
--> $DIR/lifetime_mismatch.rs:6:5
|
LL | (x, y)
| ^^^^^^
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: opaque type used twice with different lifetimes
--> $DIR/lifetime_mismatch.rs:14:5
|
LL | (i, j)
| ^^^^^^
| |
| lifetime `'x` used here
| lifetime `'y` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
--> $DIR/lifetime_mismatch.rs:14:5
|
LL | (i, j)
| ^^^^^^

error: opaque type used twice with different lifetimes
--> $DIR/lifetime_mismatch.rs:14:5
|
LL | (i, j)
| ^^^^^^
| |
| lifetime `'y` used here
| lifetime `'x` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
--> $DIR/lifetime_mismatch.rs:14:5
|
LL | (i, j)
| ^^^^^^

error: opaque type used twice with different lifetimes
--> $DIR/lifetime_mismatch.rs:14:5
--> $DIR/lifetime_mismatch.rs:13:5
|
LL | (i, j)
| ^^^^^^
Expand All @@ -69,27 +23,10 @@ LL | (i, j)
| lifetime `'y` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
--> $DIR/lifetime_mismatch.rs:14:5
|
LL | (i, j)
| ^^^^^^
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: opaque type used twice with different lifetimes
--> $DIR/lifetime_mismatch.rs:14:5
|
LL | (i, j)
| ^^^^^^
| |
| lifetime `'y` used here
| lifetime `'x` previously used here
|
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
--> $DIR/lifetime_mismatch.rs:14:5
--> $DIR/lifetime_mismatch.rs:13:5
|
LL | (i, j)
| ^^^^^^
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 6 previous errors
error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
type Foo<'a, 'b> = impl std::fmt::Debug;

fn foo<'x, 'y>(i: &'x i32, j: &'y i32) -> (Foo<'x, 'y>, Foo<'y, 'x>) {
(i, i)
(i, j)
//~^ ERROR opaque type used twice with different lifetimes
//~| ERROR opaque type used twice with different lifetimes
//~| ERROR opaque type used twice with different lifetimes
//~| ERROR opaque type used twice with different lifetimes
}

fn main() {}
Loading

0 comments on commit 7c6876f

Please sign in to comment.