Skip to content

defer opaque type errors, generally greatly reduce tainting #145115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<'_> {
Expand Down
13 changes: 1 addition & 12 deletions compiler/rustc_borrowck/src/handle_placeholders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,22 +216,11 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
placeholder_index_to_region: _,
liveness_constraints,
mut outlives_constraints,
mut member_constraints,
member_constraints,
universe_causes,
type_tests,
} = constraints;

if let Some(guar) = universal_regions.tainted_by_errors() {
debug!("Universal regions tainted by errors; removing constraints!");
// Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all
// outlives bounds that we may end up checking.
outlives_constraints = Default::default();
member_constraints = Default::default();

// Also taint the entire scope.
infcx.set_tainted_by_errors(guar);
}

let fr_static = universal_regions.fr_static;
let compute_sccs =
|constraints: &OutlivesConstraintSet<'tcx>,
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 0 additions & 5 deletions compiler/rustc_borrowck/src/nll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
78 changes: 43 additions & 35 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@ 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;
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.
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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)
}
}
11 changes: 11 additions & 0 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,17 @@ pub(crate) fn type_check<'tcx>(
)
});

// In case type check encountered an error region, we suppress unhelpful extra
// errors in by clearing out all outlives bounds that we may end up checking.
if let Some(guar) = universal_region_relations.universal_regions.encountered_re_error() {
debug!("encountered an error region; removing constraints!");
constraints.outlives_constraints = Default::default();
constraints.member_constraints = Default::default();
constraints.type_tests = Default::default();
root_cx.set_tainted_by_errors(guar);
infcx.set_tainted_by_errors(guar);
}

MirTypeckResults {
constraints,
universal_region_relations,
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_borrowck/src/universal_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ struct UniversalRegionIndices<'tcx> {

/// Whether we've encountered an error region. If we have, cancel all
/// outlives errors, as they are likely bogus.
pub tainted_by_errors: Cell<Option<ErrorGuaranteed>>,
pub encountered_re_error: Cell<Option<ErrorGuaranteed>>,
}

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -442,8 +442,8 @@ impl<'tcx> UniversalRegions<'tcx> {
self.fr_fn_body
}

pub(crate) fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
self.indices.tainted_by_errors.get()
pub(crate) fn encountered_re_error(&self) -> Option<ErrorGuaranteed> {
self.indices.encountered_re_error.get()
}
}

Expand Down Expand Up @@ -706,7 +706,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
UniversalRegionIndices {
indices: global_mapping.chain(arg_mapping).collect(),
fr_static,
tainted_by_errors: Cell::new(None),
encountered_re_error: Cell::new(None),
}
}

Expand Down Expand Up @@ -916,7 +916,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
match r.kind() {
ty::ReVar(..) => r.as_var(),
ty::ReError(guar) => {
self.tainted_by_errors.set(Some(guar));
self.encountered_re_error.set(Some(guar));
// We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the
// `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if
// errors are being emitted and 2) it leaves the happy path unaffected.
Expand Down
20 changes: 0 additions & 20 deletions tests/crashes/135528.rs

This file was deleted.

24 changes: 24 additions & 0 deletions tests/ui/impl-trait/issues/type-error-post-normalization-test.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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() {}
Loading
Loading