diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index 2c7b97afa8011..6c3142ff7df96 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -129,6 +129,12 @@ borrowck_moved_due_to_usage_in_operator = *[false] operator } +borrowck_opaque_type_lifetime_mismatch = + opaque type used twice with different lifetimes + .label = lifetime `{$arg}` used here + .prev_lifetime_label = lifetime `{$prev}` 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. + borrowck_opaque_type_non_generic_param = expected generic {$kind} parameter, found `{$ty}` .label = {STREQ($ty, "'static") -> diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index fb0e5811c2679..511844709bf85 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -8,17 +8,75 @@ use rustc_infer::infer::TyCtxtInferExt as _; use rustc_infer::traits::{Obligation, ObligationCause}; 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; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::ObligationCtxt; +use crate::session_diagnostics::LifetimeMismatchOpaqueParam; use crate::session_diagnostics::NonGenericOpaqueTypeParam; use super::RegionInferenceContext; impl<'tcx> RegionInferenceContext<'tcx> { + fn universal_name(&self, vid: ty::RegionVid) -> Option> { + 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 { + 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, 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 r2 = self.generic_arg_to_region(b).unwrap(); + if self.eval_equal(r1, r2) { + continue; + } + + infcx.tcx.sess.emit_err(LifetimeMismatchOpaqueParam { + arg: self.universal_name(r1).unwrap().into(), + prev: self.universal_name(r2).unwrap().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. /// @@ -64,6 +122,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { infcx: &InferCtxt<'tcx>, opaque_ty_decls: FxIndexMap, OpaqueHiddenType<'tcx>>, ) -> FxIndexMap> { + self.check_unique(infcx, &opaque_ty_decls); + let mut result: FxIndexMap> = FxIndexMap::default(); let member_constraints: FxIndexMap<_, _> = self @@ -79,13 +139,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut subst_regions = vec![self.universal_regions.fr_static]; - let to_universal_region = |vid, subst_regions: &mut Vec<_>| { - trace!(?vid); - let scc = self.constraint_sccs.scc(vid); - trace!(?scc); - match self.scc_values.universal_regions_outlived_by(scc).find_map(|lb| { - self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?) - }) { + let to_universal_region = + |vid, subst_regions: &mut Vec<_>| match self.universal_name(vid) { Some(region) => { let vid = self.universal_regions.to_region_vid(region); subst_regions.push(vid); @@ -99,8 +154,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { "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. diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index ca3ccf439f280..53de351fae631 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -304,6 +304,19 @@ pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> { pub param_span: Span, } +#[derive(Diagnostic)] +#[diag(borrowck_opaque_type_lifetime_mismatch)] +pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> { + pub arg: GenericArg<'tcx>, + pub prev: GenericArg<'tcx>, + #[primary_span] + #[label] + #[note] + pub span: Span, + #[label(borrowck_prev_lifetime_label)] + pub prev_span: Span, +} + #[derive(Subdiagnostic)] pub(crate) enum CaptureReasonLabel<'a> { #[label(borrowck_moved_due_to_call)] diff --git a/tests/ui/impl-trait/issue-86465.rs b/tests/ui/impl-trait/issue-86465.rs deleted file mode 100644 index a79bb6474d8ba..0000000000000 --- a/tests/ui/impl-trait/issue-86465.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(type_alias_impl_trait)] - -type X<'a, 'b> = impl std::fmt::Debug; - -fn f<'t, 'u>(a: &'t u32, b: &'u u32) -> (X<'t, 'u>, X<'u, 't>) { - (a, a) - //~^ ERROR concrete type differs from previous defining opaque type use -} - -fn main() {} diff --git a/tests/ui/impl-trait/issue-86465.stderr b/tests/ui/impl-trait/issue-86465.stderr deleted file mode 100644 index 90d6904ed6164..0000000000000 --- a/tests/ui/impl-trait/issue-86465.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/issue-86465.rs:6:5 - | -LL | (a, a) - | ^^^^^^ - | | - | expected `&'a u32`, got `&'b u32` - | this expression supplies two conflicting concrete types for the same opaque type - -error: aborting due to previous error - diff --git a/tests/ui/type-alias-impl-trait/lifetime_mismatch.rs b/tests/ui/type-alias-impl-trait/lifetime_mismatch.rs new file mode 100644 index 0000000000000..de62710f96552 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/lifetime_mismatch.rs @@ -0,0 +1,22 @@ +#![feature(type_alias_impl_trait)] + +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 +} + +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 +} + +fn main() { + let meh = 42; + let muh = 69; + println!("{:?}", bar(&meh, &muh)); +} diff --git a/tests/ui/type-alias-impl-trait/lifetime_mismatch.stderr b/tests/ui/type-alias-impl-trait/lifetime_mismatch.stderr new file mode 100644 index 0000000000000..276bcfd2e2a24 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/lifetime_mismatch.stderr @@ -0,0 +1,47 @@ +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) + | ^^^^^^ + +error: opaque type used twice with different lifetimes + --> $DIR/lifetime_mismatch.rs:13: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:13:5 + | +LL | (i, j) + | ^^^^^^ + +error: opaque type used twice with different lifetimes + --> $DIR/lifetime_mismatch.rs:13: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:13:5 + | +LL | (i, j) + | ^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs index 3f122f1060956..5fc8e563cefad 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.rs @@ -3,7 +3,9 @@ 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) //~ ERROR concrete type differs from previous + (i, i) + //~^ ERROR opaque type used twice with different lifetimes + //~| ERROR opaque type used twice with different lifetimes } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.stderr b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.stderr index 81e603e2355df..a1d8b6e88cc1d 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.stderr +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-lifetimes.stderr @@ -1,11 +1,32 @@ -error: concrete type differs from previous defining opaque type use +error: opaque type used twice with different lifetimes --> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5 | LL | (i, i) | ^^^^^^ | | - | expected `&'a i32`, got `&'b i32` - | this expression supplies two conflicting concrete types for the same opaque type + | 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/multiple-def-uses-in-one-fn-lifetimes.rs:6:5 + | +LL | (i, i) + | ^^^^^^ + +error: opaque type used twice with different lifetimes + --> $DIR/multiple-def-uses-in-one-fn-lifetimes.rs:6:5 + | +LL | (i, i) + | ^^^^^^ + | | + | 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/multiple-def-uses-in-one-fn-lifetimes.rs:6:5 + | +LL | (i, i) + | ^^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs index 83fd9a1da450b..c9c585ac7bc5a 100644 --- a/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs +++ b/tests/ui/type-alias-impl-trait/multiple-def-uses-in-one-fn-pass.rs @@ -7,15 +7,11 @@ fn f(a: A, b: B) -> (X, X) (a.clone(), a) } -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, j) +type Tait<'x> = impl Sized; +fn define<'a: 'b, 'b: 'a>(x: &'a u8, y: &'b u8) -> (Tait<'a>, Tait<'b>) { + ((), ()) } fn main() { println!("{}", as ToString>::to_string(&f(42_i32, String::new()).1)); - let meh = 42; - let muh = 69; - println!("{:?}", foo(&meh, &muh)); }