Skip to content

merge opaque types defined in nested bodies #138719

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
Mar 22, 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
7 changes: 4 additions & 3 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ mod def_use;
mod diagnostics;
mod member_constraints;
mod nll;
mod opaque_types;
mod path_utils;
mod place_ext;
mod places_conflict;
Expand Down Expand Up @@ -192,7 +193,7 @@ fn do_mir_borrowck<'tcx>(
// Compute non-lexical lifetimes.
let nll::NllOutput {
regioncx,
opaque_type_values,
concrete_opaque_types,
polonius_input,
polonius_output,
opt_closure_req,
Expand Down Expand Up @@ -222,7 +223,7 @@ fn do_mir_borrowck<'tcx>(
body,
&regioncx,
&opt_closure_req,
&opaque_type_values,
&concrete_opaque_types,
diags_buffer,
);

Expand Down Expand Up @@ -357,7 +358,7 @@ fn do_mir_borrowck<'tcx>(
let tainted_by_errors = mbcx.emit_errors();

let result = BorrowCheckResult {
concrete_opaque_types: opaque_type_values,
concrete_opaque_types: concrete_opaque_types.into_inner(),
closure_requirements: opt_closure_req,
used_mut_upvars: mbcx.used_mut_upvars,
tainted_by_errors,
Expand Down
20 changes: 11 additions & 9 deletions compiler/rustc_borrowck/src/nll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ use std::str::FromStr;
use std::{env, io};

use polonius_engine::{Algorithm, Output};
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::LocalDefId;
use rustc_index::IndexSlice;
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
use rustc_middle::mir::{
Body, ClosureOutlivesSubject, ClosureRegionRequirements, PassWhere, Promoted, create_dump_file,
dump_enabled, dump_mir,
};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt};
use rustc_middle::ty::{self, TyCtxt};
use rustc_mir_dataflow::ResultsCursor;
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData;
Expand All @@ -27,6 +25,7 @@ use tracing::{debug, instrument};
use crate::borrow_set::BorrowSet;
use crate::consumers::ConsumerOptions;
use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::polonius::PoloniusDiagnosticsContext;
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
Expand All @@ -40,7 +39,7 @@ use crate::{BorrowckInferCtxt, polonius, renumber};
/// closure requirements to propagate, and any generated errors.
pub(crate) struct NllOutput<'tcx> {
pub regioncx: RegionInferenceContext<'tcx>,
pub opaque_type_values: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
pub concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
pub polonius_input: Option<Box<PoloniusFacts>>,
pub polonius_output: Option<Box<PoloniusOutput>>,
pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
Expand Down Expand Up @@ -99,6 +98,8 @@ pub(crate) fn compute_regions<'a, 'tcx>(

let location_map = Rc::new(DenseLocationMap::new(body));

let mut concrete_opaque_types = ConcreteOpaqueTypes::default();

// Run the MIR type-checker.
let MirTypeckResults {
constraints,
Expand All @@ -116,6 +117,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
flow_inits,
move_data,
Rc::clone(&location_map),
&mut concrete_opaque_types,
);

// Create the region inference context, taking ownership of the
Expand Down Expand Up @@ -180,11 +182,11 @@ pub(crate) fn compute_regions<'a, 'tcx>(
infcx.set_tainted_by_errors(guar);
}

let remapped_opaque_tys = regioncx.infer_opaque_types(infcx, opaque_type_values);
regioncx.infer_opaque_types(infcx, opaque_type_values, &mut concrete_opaque_types);

NllOutput {
regioncx,
opaque_type_values: remapped_opaque_tys,
concrete_opaque_types,
polonius_input: polonius_facts.map(Box::new),
polonius_output,
opt_closure_req: closure_region_requirements,
Expand Down Expand Up @@ -300,7 +302,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>,
diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
) {
let tcx = infcx.tcx;
Expand Down Expand Up @@ -343,8 +345,8 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
err
};

if !opaque_type_values.is_empty() {
err.note(format!("Inferred opaque type values:\n{opaque_type_values:#?}"));
if !concrete_opaque_types.is_empty() {
err.note(format!("Inferred opaque type values:\n{concrete_opaque_types:#?}"));
}

diagnostics_buffer.buffer_non_error(err);
Expand Down
55 changes: 55 additions & 0 deletions compiler/rustc_borrowck/src/opaque_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt};

#[derive(Debug, Default)]
pub(super) struct ConcreteOpaqueTypes<'tcx> {
concrete_opaque_types: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
}

impl<'tcx> ConcreteOpaqueTypes<'tcx> {
pub(super) fn is_empty(&self) -> bool {
self.concrete_opaque_types.is_empty()
}

pub(super) fn into_inner(self) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
self.concrete_opaque_types
}

/// Insert an opaque type into the list of opaque types defined by this function
/// after mapping the hidden type to the generic parameters of the opaque type
/// definition.
pub(super) fn insert(
&mut self,
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
hidden_ty: OpaqueHiddenType<'tcx>,
) {
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
// `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
// only know that once we convert the generic parameters to those of the opaque type.
if let Some(prev) = self.concrete_opaque_types.get_mut(&def_id) {
if prev.ty != hidden_ty.ty {
let (Ok(guar) | Err(guar)) =
prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit());
prev.ty = Ty::new_error(tcx, guar);
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(hidden_ty.span);
} else {
self.concrete_opaque_types.insert(def_id, hidden_ty);
}
}

pub(super) fn extend_from_nested_body(
&mut self,
tcx: TyCtxt<'tcx>,
nested_body: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
) {
for (&def_id, &hidden_ty) in nested_body {
self.insert(tcx, def_id, hidden_ty);
}
}
}
37 changes: 8 additions & 29 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use rustc_trait_selection::traits::ObligationCtxt;
use tracing::{debug, instrument};

use super::RegionInferenceContext;
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::session_diagnostics::{LifetimeMismatchOpaqueParam, NonGenericOpaqueTypeParam};
use crate::universal_regions::RegionClassification;

Expand Down Expand Up @@ -67,8 +68,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self,
infcx: &InferCtxt<'tcx>,
opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
) {
let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
FxIndexMap::default();

Expand Down Expand Up @@ -143,33 +144,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
continue;
}
}
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
// `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
// only know that once we convert the generic parameters to those of the opaque type.
if let Some(prev) = result.get_mut(&opaque_type_key.def_id) {
if prev.ty != ty {
let guar = ty.error_reported().err().unwrap_or_else(|| {
let (Ok(e) | Err(e)) = prev
.build_mismatch_error(
&OpaqueHiddenType { ty, span: concrete_type.span },
infcx.tcx,
)
.map(|d| d.emit());
e
});
prev.ty = Ty::new_error(infcx.tcx, guar);
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(concrete_type.span);
} else {
result.insert(
opaque_type_key.def_id,
OpaqueHiddenType { ty, span: concrete_type.span },
);
}

concrete_opaque_types.insert(
infcx.tcx,
opaque_type_key.def_id,
OpaqueHiddenType { ty, span: concrete_type.span },
);
// 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
Expand All @@ -193,7 +173,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
});
}
}
result
}

/// Map the regions in the type to named regions. This is similar to what
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use crate::borrow_set::BorrowSet;
use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet};
use crate::diagnostics::UniverseInfo;
use crate::member_constraints::MemberConstraintSet;
use crate::opaque_types::ConcreteOpaqueTypes;
use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable};
use crate::polonius::{PoloniusContext, PoloniusLivenessContext};
use crate::region_infer::TypeTest;
Expand Down Expand Up @@ -111,6 +112,7 @@ pub(crate) fn type_check<'a, 'tcx>(
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
move_data: &MoveData<'tcx>,
location_map: Rc<DenseLocationMap>,
concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>,
) -> MirTypeckResults<'tcx> {
let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body);
let mut constraints = MirTypeckRegionConstraints {
Expand Down Expand Up @@ -165,6 +167,7 @@ pub(crate) fn type_check<'a, 'tcx>(
polonius_facts,
borrow_set,
constraints: &mut constraints,
concrete_opaque_types,
polonius_liveness,
};

Expand Down Expand Up @@ -230,6 +233,7 @@ struct TypeChecker<'a, 'tcx> {
polonius_facts: &'a mut Option<PoloniusFacts>,
borrow_set: &'a BorrowSet<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
concrete_opaque_types: &'a mut ConcreteOpaqueTypes<'tcx>,
/// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints.
polonius_liveness: Option<PoloniusLivenessContext>,
}
Expand Down Expand Up @@ -2499,7 +2503,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
args: GenericArgsRef<'tcx>,
locations: Locations,
) -> ty::InstantiatedPredicates<'tcx> {
if let Some(closure_requirements) = &tcx.mir_borrowck(def_id).closure_requirements {
let closure_borrowck_results = tcx.mir_borrowck(def_id);
self.concrete_opaque_types
.extend_from_nested_body(tcx, &closure_borrowck_results.concrete_opaque_types);

if let Some(closure_requirements) = &closure_borrowck_results.closure_requirements {
constraint_conversion::ConstraintConversion::new(
self.infcx,
self.universal_regions,
Expand Down
14 changes: 1 addition & 13 deletions compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,6 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
self.tcx
}
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
if let hir::ExprKind::Closure(closure) = ex.kind {
self.check(closure.def_id);
}
intravisit::walk_expr(self, ex);
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
Expand Down Expand Up @@ -371,14 +368,8 @@ impl RpitConstraintChecker<'_> {
// Use borrowck to get the type with unerased regions.
let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
debug!(?concrete_opaque_types);
for (&def_id, &concrete_type) in concrete_opaque_types {
if def_id != self.def_id {
// Ignore constraints for other opaque types.
continue;
}

if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
debug!(?concrete_type, "found constraint");

if concrete_type.ty != self.found.ty {
if let Ok(d) = self.found.build_mismatch_error(&concrete_type, self.tcx) {
d.emit();
Expand All @@ -395,9 +386,6 @@ impl<'tcx> intravisit::Visitor<'tcx> for RpitConstraintChecker<'tcx> {
self.tcx
}
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
if let hir::ExprKind::Closure(closure) = ex.kind {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was previously incorrect as we'd have also looked into inline constants :<

self.check(closure.def_id);
}
intravisit::walk_expr(self, ex);
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
Expand Down
12 changes: 0 additions & 12 deletions tests/crashes/122904.rs

This file was deleted.

3 changes: 1 addition & 2 deletions tests/ui/impl-trait/impl-fn-predefined-lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
use std::fmt::Debug;

fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) {
//~^ ERROR cannot resolve opaque type
//~| WARNING elided lifetime has a name
//~^ WARNING elided lifetime has a name
|x| x
//~^ ERROR expected generic lifetime parameter, found `'_`
}
Expand Down
18 changes: 4 additions & 14 deletions tests/ui/impl-trait/impl-fn-predefined-lifetimes.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,14 @@ LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) {
= note: `#[warn(elided_named_lifetimes)]` on by default

error[E0792]: expected generic lifetime parameter, found `'_`
--> $DIR/impl-fn-predefined-lifetimes.rs:7:9
--> $DIR/impl-fn-predefined-lifetimes.rs:6:9
|
LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) {
| -- this generic parameter must be used with a generic lifetime parameter
...
LL |
LL | |x| x
| ^

error[E0720]: cannot resolve opaque type
--> $DIR/impl-fn-predefined-lifetimes.rs:4:35
|
LL | fn a<'a>() -> impl Fn(&'a u8) -> (impl Debug + '_) {
| ^^^^^^^^^^^^^^^ recursive opaque type
...
LL | |x| x
| ----- returning here with type `{closure@$DIR/impl-fn-predefined-lifetimes.rs:7:5: 7:8}`

error: aborting due to 2 previous errors; 1 warning emitted
error: aborting due to 1 previous error; 1 warning emitted

Some errors have detailed explanations: E0720, E0792.
For more information about an error, try `rustc --explain E0720`.
For more information about this error, try `rustc --explain E0792`.
1 change: 0 additions & 1 deletion tests/ui/impl-trait/issues/issue-86800.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ impl Context {
f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>,
) -> TransactionResult<O> {
//~^ ERROR expected generic lifetime parameter, found `'_`
//~| ERROR: item does not constrain
let mut conn = Connection {};
let mut transaction = TestTransaction { conn: &mut conn };
f(&mut transaction).await
Expand Down
Loading
Loading