borrowck: defer opaque type errors

This commit is contained in:
lcnr 2025-08-08 19:18:17 +02:00
parent 8b95291cd4
commit 4eee55691a
18 changed files with 193 additions and 84 deletions

View file

@ -51,7 +51,7 @@ mod conflict_errors;
mod explain_borrow;
mod move_errors;
mod mutability_errors;
mod opaque_suggestions;
mod opaque_types;
mod region_errors;
pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo};

View file

@ -18,9 +18,28 @@ use rustc_trait_selection::errors::impl_trait_overcapture_suggestion;
use crate::MirBorrowckCtxt;
use crate::borrow_set::BorrowData;
use crate::consumers::RegionInferenceContext;
use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
use crate::type_check::Locations;
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
pub(crate) fn report_opaque_type_errors(&mut self, errors: Vec<DeferredOpaqueTypeError<'tcx>>) {
if errors.is_empty() {
return;
}
let mut guar = None;
for error in errors {
guar = Some(match error {
DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(self.infcx),
DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => {
self.infcx.dcx().emit_err(err)
}
});
}
let guar = guar.unwrap();
self.root_cx.set_tainted_by_errors(guar);
self.infcx.set_tainted_by_errors(guar);
}
/// Try to note when an opaque is involved in a borrowck error and that
/// opaque captures lifetimes due to edition 2024.
// FIXME: This code is otherwise somewhat general, and could easily be adapted

View file

@ -92,9 +92,6 @@ impl<'tcx> RegionErrors<'tcx> {
) -> impl Iterator<Item = (RegionErrorKind<'tcx>, ErrorGuaranteed)> {
self.0.into_iter()
}
pub(crate) fn has_errors(&self) -> Option<ErrorGuaranteed> {
self.0.get(0).map(|x| x.1)
}
}
impl std::fmt::Debug for RegionErrors<'_> {

View file

@ -375,7 +375,7 @@ fn do_mir_borrowck<'tcx>(
polonius_context,
);
regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values);
let opaque_type_errors = regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values);
// Dump MIR results into a file, if that is enabled. This lets us
// write unit-tests, as well as helping with debugging.
@ -471,7 +471,11 @@ fn do_mir_borrowck<'tcx>(
};
// Compute and report region errors, if any.
mbcx.report_region_errors(nll_errors);
if nll_errors.is_empty() {
mbcx.report_opaque_type_errors(opaque_type_errors);
} else {
mbcx.report_region_errors(nll_errors);
}
let (mut flow_analysis, flow_entry_states) =
get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);

View file

@ -148,11 +148,6 @@ pub(crate) fn compute_regions<'tcx>(
let (closure_region_requirements, nll_errors) =
regioncx.solve(infcx, body, polonius_output.clone());
if let Some(guar) = nll_errors.has_errors() {
// Suppress unhelpful extra errors in `infer_opaque_types`.
infcx.set_tainted_by_errors(guar);
}
NllOutput {
regioncx,
polonius_input: polonius_facts.map(Box::new),

View file

@ -44,7 +44,7 @@ use crate::{
mod dump_mir;
mod graphviz;
mod opaque_types;
pub(crate) mod opaque_types;
mod reverse_sccs;
pub(crate) mod values;

View file

@ -6,7 +6,9 @@ use rustc_middle::ty::{
TypeVisitableExt, fold_regions,
};
use rustc_span::Span;
use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
use rustc_trait_selection::opaque_types::{
InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
};
use tracing::{debug, instrument};
use super::RegionInferenceContext;
@ -14,6 +16,11 @@ use crate::BorrowCheckRootCtxt;
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
use crate::universal_regions::RegionClassification;
pub(crate) enum DeferredOpaqueTypeError<'tcx> {
InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>),
}
impl<'tcx> RegionInferenceContext<'tcx> {
/// 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.
@ -58,13 +65,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
///
/// [rustc-dev-guide chapter]:
/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
#[instrument(level = "debug", skip(self, root_cx, infcx), ret)]
#[instrument(level = "debug", skip(self, root_cx, infcx))]
pub(crate) fn infer_opaque_types(
&self,
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
infcx: &InferCtxt<'tcx>,
opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
) {
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let mut errors = Vec::new();
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
FxIndexMap::default();
@ -124,8 +132,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
});
debug!(?concrete_type);
let ty =
infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type);
let ty = match infcx
.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type)
{
Ok(ty) => ty,
Err(err) => {
errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err));
continue;
}
};
// Sometimes, when the hidden type is an inference variable, it can happen that
// the hidden type becomes the opaque type itself. In this case, this was an opaque
@ -149,25 +164,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// 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)
if 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,
});
errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(
LifetimeMismatchOpaqueParam {
arg: arg1,
prev: arg2,
span: prev_span,
prev_span: concrete_type.span,
},
));
}
}
errors
}
/// Map the regions in the type to named regions. This is similar to what
@ -260,19 +277,13 @@ impl<'tcx> InferCtxt<'tcx> {
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
instantiated_ty: OpaqueHiddenType<'tcx>,
) -> Ty<'tcx> {
if let Some(e) = self.tainted_by_errors() {
return Ty::new_error(self.tcx, e);
}
if let Err(err) = check_opaque_type_parameter_valid(
) -> Result<Ty<'tcx>, InvalidOpaqueTypeArgs<'tcx>> {
check_opaque_type_parameter_valid(
self,
opaque_type_key,
instantiated_ty.span,
DefiningScopeKind::MirBorrowck,
) {
return Ty::new_error(self.tcx, err.report(self));
}
)?;
let definition_ty = instantiated_ty
.remap_generic_params_to_declaration_params(
@ -282,10 +293,7 @@ impl<'tcx> InferCtxt<'tcx> {
)
.ty;
if let Err(e) = definition_ty.error_reported() {
return Ty::new_error(self.tcx, e);
}
definition_ty
definition_ty.error_reported()?;
Ok(definition_ty)
}
}

View file

@ -1,20 +0,0 @@
//@ known-bug: #135528
//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes
#![feature(type_alias_impl_trait)]
type Tait = impl Copy;
fn set(x: &isize) -> isize {
*x
}
#[define_opaque(Tait)]
fn d(x: Tait) {
set(x);
}
#[define_opaque(Tait)]
fn other_define() -> Tait {
()
}
fn main() {}

View file

@ -0,0 +1,24 @@
//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes
// This previously introduced a `{type_error}`` in the MIR body
// during the `PostAnalysisNormalize` pass. While the underlying issue
// #135528 did not get fixed, this reproducer no longer ICEs.
#![feature(type_alias_impl_trait)]
type Tait = impl Copy;
fn set(x: &isize) -> isize {
*x
}
#[define_opaque(Tait)]
fn d(x: Tait) {
set(x);
}
#[define_opaque(Tait)]
fn other_define() -> Tait {
() //~^ ERROR concrete type differs from previous defining opaque type use
}
fn main() {}

View file

@ -0,0 +1,14 @@
error: concrete type differs from previous defining opaque type use
--> $DIR/type-error-post-normalization-test.rs:20:22
|
LL | fn other_define() -> Tait {
| ^^^^ expected `&isize`, got `()`
|
note: previous use here
--> $DIR/type-error-post-normalization-test.rs:16:9
|
LL | set(x);
| ^
error: aborting due to 1 previous error

View file

@ -41,6 +41,7 @@ mod capture_tait_complex_pass {
#[define_opaque(Opq2)]
fn test() -> Opq2 {}
//~^ ERROR: expected generic lifetime parameter, found `'a`
//~| ERROR: expected generic lifetime parameter, found `'b`
}
// Same as the above, but make sure that different placeholder regions are not equal.
@ -80,6 +81,7 @@ mod constrain_pass {
#[define_opaque(Opq2)]
fn test() -> Opq2 {}
//~^ ERROR: expected generic lifetime parameter, found `'a`
//~| ERROR: expected generic lifetime parameter, found `'a`
}
fn main() {}

View file

@ -36,8 +36,17 @@ LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'b>>; // <- Note 'b
LL | fn test() -> Opq2 {}
| ^^
error[E0792]: expected generic lifetime parameter, found `'b`
--> $DIR/higher-ranked-regions-basic.rs:42:23
|
LL | type Opq0<'a> = impl Sized;
| -- this generic parameter must be used with a generic lifetime parameter
...
LL | fn test() -> Opq2 {}
| ^^
error[E0700]: hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds
--> $DIR/higher-ranked-regions-basic.rs:53:23
--> $DIR/higher-ranked-regions-basic.rs:54:23
|
LL | type Opq0<'a> = impl Sized;
| ---------- opaque type defined here
@ -48,7 +57,7 @@ LL | fn test() -> Opq2 {}
| ^^
error[E0792]: expected generic lifetime parameter, found `'a`
--> $DIR/higher-ranked-regions-basic.rs:62:65
--> $DIR/higher-ranked-regions-basic.rs:63:65
|
LL | type Opq0<'a, 'b> = impl Sized;
| -- this generic parameter must be used with a generic lifetime parameter
@ -57,7 +66,7 @@ LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {}
| ^^
error[E0792]: expected generic lifetime parameter, found `'a`
--> $DIR/higher-ranked-regions-basic.rs:71:60
--> $DIR/higher-ranked-regions-basic.rs:72:60
|
LL | type Opq0<'a, 'b> = impl Sized;
| -- this generic parameter must be used with a generic lifetime parameter
@ -66,7 +75,7 @@ LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {}
| ^^
error[E0792]: expected generic lifetime parameter, found `'a`
--> $DIR/higher-ranked-regions-basic.rs:81:23
--> $DIR/higher-ranked-regions-basic.rs:82:23
|
LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>;
| -- this generic parameter must be used with a generic lifetime parameter
@ -74,7 +83,16 @@ LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>;
LL | fn test() -> Opq2 {}
| ^^
error: aborting due to 8 previous errors
error[E0792]: expected generic lifetime parameter, found `'a`
--> $DIR/higher-ranked-regions-basic.rs:82:23
|
LL | type Opq0<'a, 'b> = impl Sized;
| -- this generic parameter must be used with a generic lifetime parameter
...
LL | fn test() -> Opq2 {}
| ^^
error: aborting due to 10 previous errors
Some errors have detailed explanations: E0700, E0792.
For more information about an error, try `rustc --explain E0700`.

View file

@ -8,7 +8,9 @@ type Opaque<'a> = impl Sized + 'a;
#[define_opaque(Opaque)]
fn test(s: &str) -> (impl Fn(&str) -> Opaque<'_>, impl Fn(&str) -> Opaque<'_>) {
(id, id) //~ ERROR expected generic lifetime parameter, found `'_`
(id, id)
//~^ ERROR expected generic lifetime parameter, found `'_`
//~| ERROR expected generic lifetime parameter, found `'_`
}
fn id2<'a, 'b>(s: (&'a str, &'b str)) -> (&'a str, &'b str) {
@ -19,7 +21,9 @@ type Opaque2<'a> = impl Sized + 'a;
#[define_opaque(Opaque2)]
fn test2() -> impl for<'a, 'b> Fn((&'a str, &'b str)) -> (Opaque2<'a>, Opaque2<'b>) {
id2 //~ ERROR expected generic lifetime parameter, found `'a`
id2
//~^ ERROR expected generic lifetime parameter, found `'a`
//~| ERROR expected generic lifetime parameter, found `'b`
}
type Opaque3<'a> = impl Sized + 'a;

View file

@ -7,8 +7,28 @@ LL | type Opaque<'a> = impl Sized + 'a;
LL | (id, id)
| ^^^^^^^^
error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/hkl_forbidden.rs:11:5
|
LL | type Opaque<'a> = impl Sized + 'a;
| -- this generic parameter must be used with a generic lifetime parameter
...
LL | (id, id)
| ^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0792]: expected generic lifetime parameter, found `'a`
--> $DIR/hkl_forbidden.rs:22:5
--> $DIR/hkl_forbidden.rs:24:5
|
LL | type Opaque2<'a> = impl Sized + 'a;
| -- this generic parameter must be used with a generic lifetime parameter
...
LL | id2
| ^^^
error[E0792]: expected generic lifetime parameter, found `'b`
--> $DIR/hkl_forbidden.rs:24:5
|
LL | type Opaque2<'a> = impl Sized + 'a;
| -- this generic parameter must be used with a generic lifetime parameter
@ -17,7 +37,7 @@ LL | id2
| ^^^
error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/hkl_forbidden.rs:29:5
--> $DIR/hkl_forbidden.rs:33:5
|
LL | type Opaque3<'a> = impl Sized + 'a;
| -- this generic parameter must be used with a generic lifetime parameter
@ -26,7 +46,7 @@ LL | (id, s)
| ^^^^^^^
error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/hkl_forbidden.rs:35:5
--> $DIR/hkl_forbidden.rs:39:5
|
LL | type Opaque4<'a> = impl Sized + 'a;
| -- this generic parameter must be used with a generic lifetime parameter
@ -35,7 +55,7 @@ LL | (s, id)
| ^^^^^^^
error[E0792]: expected generic lifetime parameter, found `'a`
--> $DIR/hkl_forbidden.rs:41:5
--> $DIR/hkl_forbidden.rs:45:5
|
LL | type Inner<'a> = impl Sized;
| -- this generic parameter must be used with a generic lifetime parameter
@ -43,6 +63,6 @@ LL | type Inner<'a> = impl Sized;
LL | |x| x
| ^^^^^
error: aborting due to 5 previous errors
error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0792`.

View file

@ -11,7 +11,9 @@ type Opaque<'a> = impl Sized + 'a;
#[define_opaque(Opaque)]
fn test(s: &str) -> (impl Fn(&str) -> Opaque<'_>, impl Fn(&str) -> Opaque<'_>) {
(id, id) //~ ERROR: expected generic lifetime parameter, found `'_`
(id, id)
//~^ ERROR: expected generic lifetime parameter, found `'_`
//~| ERROR: expected generic lifetime parameter, found `'_`
}
fn main() {}

View file

@ -7,6 +7,17 @@ LL | type Opaque<'a> = impl Sized + 'a;
LL | (id, id)
| ^^^^^^^^
error: aborting due to 1 previous error
error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/param_mismatch2.rs:14:5
|
LL | type Opaque<'a> = impl Sized + 'a;
| -- this generic parameter must be used with a generic lifetime parameter
...
LL | (id, id)
| ^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0792`.

View file

@ -11,7 +11,9 @@ type Opaque<'a> = impl Sized + 'a;
#[define_opaque(Opaque)]
fn test() -> impl for<'a, 'b> Fn((&'a str, &'b str)) -> (Opaque<'a>, Opaque<'b>) {
id2 //~ ERROR expected generic lifetime parameter, found `'a`
id2
//~^ ERROR expected generic lifetime parameter, found `'a`
//~| ERROR expected generic lifetime parameter, found `'b`
}
fn id(s: &str) -> &str {

View file

@ -7,8 +7,17 @@ LL | type Opaque<'a> = impl Sized + 'a;
LL | id2
| ^^^
error[E0792]: expected generic lifetime parameter, found `'b`
--> $DIR/param_mismatch3.rs:14:5
|
LL | type Opaque<'a> = impl Sized + 'a;
| -- this generic parameter must be used with a generic lifetime parameter
...
LL | id2
| ^^^
error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/param_mismatch3.rs:25:5
--> $DIR/param_mismatch3.rs:27:5
|
LL | type Opaque2<'a> = impl Sized + 'a;
| -- this generic parameter must be used with a generic lifetime parameter
@ -16,6 +25,6 @@ LL | type Opaque2<'a> = impl Sized + 'a;
LL | (id, s)
| ^^^^^^^
error: aborting due to 2 previous errors
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0792`.