Skip to content

Commit 3ef7f68

Browse files
committed
Prevent opaque types being instantiated twice with different regions within the same function
1 parent ef32456 commit 3ef7f68

17 files changed

+298
-54
lines changed

compiler/rustc_borrowck/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ borrowck_moved_due_to_usage_in_operator =
129129
*[false] operator
130130
}
131131
132+
borrowck_opaque_type_lifetime_mismatch =
133+
opaque type used twice with different lifetimes
134+
.label = lifetime `{$arg}` used here
135+
.prev_lifetime_label = lifetime `{$prev}` previously used here
136+
.note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
137+
132138
borrowck_opaque_type_non_generic_param =
133139
expected generic {$kind} parameter, found `{$ty}`
134140
.label = {STREQ($ty, "'static") ->

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

+76-20
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,77 @@ use rustc_infer::traits::{Obligation, ObligationCause};
99
use rustc_macros::extension;
1010
use rustc_middle::traits::DefiningAnchor;
1111
use rustc_middle::ty::visit::TypeVisitableExt;
12+
use rustc_middle::ty::RegionVid;
1213
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable};
1314
use rustc_middle::ty::{GenericArgKind, GenericArgs};
1415
use rustc_span::Span;
1516
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
1617
use rustc_trait_selection::traits::ObligationCtxt;
1718

19+
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
1820
use crate::session_diagnostics::NonGenericOpaqueTypeParam;
1921

2022
use super::RegionInferenceContext;
2123

2224
impl<'tcx> RegionInferenceContext<'tcx> {
25+
fn universal_name(&self, vid: ty::RegionVid) -> Option<ty::Region<'tcx>> {
26+
let scc = self.constraint_sccs.scc(vid);
27+
self.scc_values
28+
.universal_regions_outlived_by(scc)
29+
.find_map(|lb| self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?))
30+
}
31+
32+
fn generic_arg_to_region(&self, arg: ty::GenericArg<'tcx>) -> Option<RegionVid> {
33+
let region = arg.as_region()?;
34+
35+
if let ty::RePlaceholder(..) = region.kind() {
36+
None
37+
} else {
38+
Some(self.to_region_vid(region))
39+
}
40+
}
41+
42+
/// Check that all opaque types have the same region parameters if they have the same
43+
/// non-region parameters. This is necessary because within the new solver we perform various query operations
44+
/// modulo regions, and thus could unsoundly select some impls that don't hold.
45+
fn check_unique(
46+
&self,
47+
infcx: &InferCtxt<'tcx>,
48+
opaque_ty_decls: &FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
49+
) {
50+
for (i, (a, a_ty)) in opaque_ty_decls.iter().enumerate() {
51+
for (b, b_ty) in opaque_ty_decls.iter().skip(i + 1) {
52+
if a.def_id != b.def_id {
53+
continue;
54+
}
55+
// Non-lifetime params differ -> ok
56+
if infcx.tcx.erase_regions(a.args) != infcx.tcx.erase_regions(b.args) {
57+
continue;
58+
}
59+
trace!(?a, ?b);
60+
for (a, b) in a.args.iter().zip(b.args) {
61+
trace!(?a, ?b);
62+
let Some(r1) = self.generic_arg_to_region(a) else {
63+
continue;
64+
};
65+
let Some(r2) = self.generic_arg_to_region(b) else {
66+
continue;
67+
};
68+
if self.eval_equal(r1, r2) {
69+
continue;
70+
}
71+
72+
infcx.dcx().emit_err(LifetimeMismatchOpaqueParam {
73+
arg: self.universal_name(r1).unwrap().into(),
74+
prev: self.universal_name(r2).unwrap().into(),
75+
span: a_ty.span,
76+
prev_span: b_ty.span,
77+
});
78+
}
79+
}
80+
}
81+
}
82+
2383
/// Resolve any opaque types that were encountered while borrow checking
2484
/// this item. This is then used to get the type in the `type_of` query.
2585
///
@@ -65,6 +125,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
65125
infcx: &InferCtxt<'tcx>,
66126
opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
67127
) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
128+
self.check_unique(infcx, &opaque_ty_decls);
129+
68130
let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
69131

70132
let member_constraints: FxIndexMap<_, _> = self
@@ -80,26 +142,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
80142

81143
let mut arg_regions = vec![self.universal_regions.fr_static];
82144

83-
let to_universal_region = |vid, arg_regions: &mut Vec<_>| {
84-
trace!(?vid);
85-
let scc = self.constraint_sccs.scc(vid);
86-
trace!(?scc);
87-
match self.scc_values.universal_regions_outlived_by(scc).find_map(|lb| {
88-
self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?)
89-
}) {
90-
Some(region) => {
91-
let vid = self.universal_regions.to_region_vid(region);
92-
arg_regions.push(vid);
93-
region
94-
}
95-
None => {
96-
arg_regions.push(vid);
97-
ty::Region::new_error_with_message(
98-
infcx.tcx,
99-
concrete_type.span,
100-
"opaque type with non-universal region args",
101-
)
102-
}
145+
let to_universal_region = |vid, arg_regions: &mut Vec<_>| match self.universal_name(vid)
146+
{
147+
Some(region) => {
148+
let vid = self.universal_regions.to_region_vid(region);
149+
arg_regions.push(vid);
150+
region
151+
}
152+
None => {
153+
arg_regions.push(vid);
154+
ty::Region::new_error_with_message(
155+
infcx.tcx,
156+
concrete_type.span,
157+
"opaque type with non-universal region args",
158+
)
103159
}
104160
};
105161

compiler/rustc_borrowck/src/session_diagnostics.rs

+13
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,19 @@ pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
304304
pub param_span: Span,
305305
}
306306

307+
#[derive(Diagnostic)]
308+
#[diag(borrowck_opaque_type_lifetime_mismatch)]
309+
pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> {
310+
pub arg: GenericArg<'tcx>,
311+
pub prev: GenericArg<'tcx>,
312+
#[primary_span]
313+
#[label]
314+
#[note]
315+
pub span: Span,
316+
#[label(borrowck_prev_lifetime_label)]
317+
pub prev_span: Span,
318+
}
319+
307320
#[derive(Subdiagnostic)]
308321
pub(crate) enum CaptureReasonLabel<'a> {
309322
#[label(borrowck_moved_due_to_call)]

src/tools/tidy/src/issues.txt

-1
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,6 @@
12021202
"ui/impl-trait/issue-56445.rs",
12031203
"ui/impl-trait/issue-68532.rs",
12041204
"ui/impl-trait/issue-72911.rs",
1205-
"ui/impl-trait/issue-86465.rs",
12061205
"ui/impl-trait/issue-87450.rs",
12071206
"ui/impl-trait/issue-99073-2.rs",
12081207
"ui/impl-trait/issue-99073.rs",

tests/ui/impl-trait/issue-86465.rs

-10
This file was deleted.

tests/ui/impl-trait/issue-86465.stderr

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![feature(type_alias_impl_trait)]
2+
3+
type Foo<'a> = impl Sized;
4+
5+
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> (Foo<'a>, Foo<'b>) {
6+
(x, y)
7+
//~^ ERROR opaque type used twice with different lifetimes
8+
}
9+
10+
type Bar<'a, 'b> = impl std::fmt::Debug;
11+
12+
fn bar<'x, 'y>(i: &'x i32, j: &'y i32) -> (Bar<'x, 'y>, Bar<'y, 'x>) {
13+
(i, j)
14+
//~^ ERROR opaque type used twice with different lifetimes
15+
//~| ERROR opaque type used twice with different lifetimes
16+
}
17+
18+
fn main() {
19+
let meh = 42;
20+
let muh = 69;
21+
println!("{:?}", bar(&meh, &muh));
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error: opaque type used twice with different lifetimes
2+
--> $DIR/lifetime_mismatch.rs:6:5
3+
|
4+
LL | (x, y)
5+
| ^^^^^^
6+
| |
7+
| lifetime `'a` used here
8+
| lifetime `'b` previously used here
9+
|
10+
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
11+
--> $DIR/lifetime_mismatch.rs:6:5
12+
|
13+
LL | (x, y)
14+
| ^^^^^^
15+
16+
error: opaque type used twice with different lifetimes
17+
--> $DIR/lifetime_mismatch.rs:13:5
18+
|
19+
LL | (i, j)
20+
| ^^^^^^
21+
| |
22+
| lifetime `'x` used here
23+
| lifetime `'y` previously used here
24+
|
25+
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
26+
--> $DIR/lifetime_mismatch.rs:13:5
27+
|
28+
LL | (i, j)
29+
| ^^^^^^
30+
31+
error: opaque type used twice with different lifetimes
32+
--> $DIR/lifetime_mismatch.rs:13:5
33+
|
34+
LL | (i, j)
35+
| ^^^^^^
36+
| |
37+
| lifetime `'y` used here
38+
| lifetime `'x` previously used here
39+
|
40+
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
41+
--> $DIR/lifetime_mismatch.rs:13:5
42+
|
43+
LL | (i, j)
44+
| ^^^^^^
45+
46+
error: aborting due to 3 previous errors
47+

tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
type Foo<'a, 'b> = impl std::fmt::Debug;
44

55
fn foo<'x, 'y>(i: &'x i32, j: &'y i32) -> (Foo<'x, 'y>, Foo<'y, 'x>) {
6-
(i, i) //~ ERROR concrete type differs from previous
6+
(i, i)
7+
//~^ ERROR opaque type used twice with different lifetimes
8+
//~| ERROR opaque type used twice with different lifetimes
79
}
810

911
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
1-
error: concrete type differs from previous defining opaque type use
1+
error: opaque type used twice with different lifetimes
22
--> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5
33
|
44
LL | (i, i)
55
| ^^^^^^
66
| |
7-
| expected `&'a i32`, got `&'b i32`
8-
| this expression supplies two conflicting concrete types for the same opaque type
7+
| lifetime `'x` used here
8+
| lifetime `'y` previously used here
9+
|
10+
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
11+
--> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5
12+
|
13+
LL | (i, i)
14+
| ^^^^^^
15+
16+
error: opaque type used twice with different lifetimes
17+
--> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5
18+
|
19+
LL | (i, i)
20+
| ^^^^^^
21+
| |
22+
| lifetime `'y` used here
23+
| lifetime `'x` previously used here
24+
|
25+
note: if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
26+
--> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5
27+
|
28+
LL | (i, i)
29+
| ^^^^^^
930

10-
error: aborting due to 1 previous error
31+
error: aborting due to 2 previous errors
1132

tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,11 @@ fn f<A: ToString + Clone, B: ToString + Clone>(a: A, b: B) -> (X<A, B>, X<A, B>)
77
(a.clone(), a)
88
}
99

10-
type Foo<'a, 'b> = impl std::fmt::Debug;
11-
12-
fn foo<'x, 'y>(i: &'x i32, j: &'y i32) -> (Foo<'x, 'y>, Foo<'y, 'x>) {
13-
(i, j)
10+
type Tait<'x> = impl Sized;
11+
fn define<'a: 'b, 'b: 'a>(x: &'a u8, y: &'b u8) -> (Tait<'a>, Tait<'b>) {
12+
((), ())
1413
}
1514

1615
fn main() {
1716
println!("{}", <X<_, _> as ToString>::to_string(&f(42_i32, String::new()).1));
18-
let meh = 42;
19-
let muh = 69;
20-
println!("{:?}", foo(&meh, &muh));
2117
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//! This test checks that when checking for opaque types that
2+
//! only differ in lifetimes, we handle the case of non-generic
3+
//! regions correctly.
4+
#![feature(type_alias_impl_trait)]
5+
6+
fn id(s: &str) -> &str {
7+
s
8+
}
9+
type Opaque<'a> = impl Sized + 'a;
10+
// The second `Opaque<'_>` has a higher kinded lifetime, not a generic parameter
11+
fn test(s: &str) -> (Opaque<'_>, impl Fn(&str) -> Opaque<'_>) {
12+
(s, id)
13+
//~^ ERROR: expected generic lifetime parameter, found `'_`
14+
}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0792]: expected generic lifetime parameter, found `'_`
2+
--> $DIR/param_mismatch.rs:12:5
3+
|
4+
LL | type Opaque<'a> = impl Sized + 'a;
5+
| -- this generic parameter must be used with a generic lifetime parameter
6+
...
7+
LL | (s, id)
8+
| ^^^^^^^
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0792`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//! This test checks that when checking for opaque types that
2+
//! only differ in lifetimes, we handle the case of non-generic
3+
//! regions correctly.
4+
#![feature(type_alias_impl_trait)]
5+
6+
fn id(s: &str) -> &str {
7+
s
8+
}
9+
10+
type Opaque<'a> = impl Sized + 'a;
11+
12+
fn test(s: &str) -> (impl Fn(&str) -> Opaque<'_>, impl Fn(&str) -> Opaque<'_>) {
13+
(id, id) //~ ERROR: expected generic lifetime parameter, found `'_`
14+
}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0792]: expected generic lifetime parameter, found `'_`
2+
--> $DIR/param_mismatch2.rs:13:5
3+
|
4+
LL | type Opaque<'a> = impl Sized + 'a;
5+
| -- this generic parameter must be used with a generic lifetime parameter
6+
...
7+
LL | (id, id)
8+
| ^^^^^^^^
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0792`.

0 commit comments

Comments
 (0)