Skip to content

Commit 9b1fcee

Browse files
committed
Prevent opaque types being instantiated twice with different regions within the same function
1 parent a00c09e commit 9b1fcee

10 files changed

+178
-44
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

+62-9
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,68 @@ use rustc_span::Span;
1313
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
1414
use rustc_trait_selection::traits::ObligationCtxt;
1515

16+
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
1617
use crate::session_diagnostics::NonGenericOpaqueTypeParam;
1718

1819
use super::RegionInferenceContext;
1920

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

68127
let member_constraints: FxIndexMap<_, _> = self
@@ -78,13 +137,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
78137

79138
let mut subst_regions = vec![self.universal_regions.fr_static];
80139

81-
let to_universal_region = |vid, subst_regions: &mut Vec<_>| {
82-
trace!(?vid);
83-
let scc = self.constraint_sccs.scc(vid);
84-
trace!(?scc);
85-
match self.scc_values.universal_regions_outlived_by(scc).find_map(|lb| {
86-
self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?)
87-
}) {
140+
let to_universal_region =
141+
|vid, subst_regions: &mut Vec<_>| match self.universal_name(vid) {
88142
Some(region) => {
89143
let vid = self.universal_regions.to_region_vid(region);
90144
subst_regions.push(vid);
@@ -98,8 +152,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
98152
"opaque type with non-universal region args",
99153
)
100154
}
101-
}
102-
};
155+
};
103156

104157
// Start by inserting universal regions from the member_constraint choice regions.
105158
// This will ensure they get precedence when folding the regions in the concrete type.

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)]

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 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

-9
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@ 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)
14-
}
15-
1610
fn main() {
1711
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));
2112
}

0 commit comments

Comments
 (0)