Rollup merge of #124809 - lcnr:prepopulate-opaques, r=compiler-errors

borrowck: prepopulate opaque storage more eagerly

otherwise we ICE due to ambiguity when normalizing while computing implied bounds.

r? ``@compiler-errors``
This commit is contained in:
Matthias Krüger 2024-05-06 21:46:06 +02:00 committed by GitHub
commit 284b5530b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 115 additions and 165 deletions

View File

@ -2,7 +2,6 @@
use rustc_hir::def_id::LocalDefId;
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::{Body, Promoted};
use rustc_middle::ty::TyCtxt;
use std::rc::Rc;
@ -105,8 +104,7 @@ pub fn get_body_with_borrowck_facts(
options: ConsumerOptions,
) -> BodyWithBorrowckFacts<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
let infcx = tcx.infer_ctxt().with_opaque_type_inference(def).build();
let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexSlice<_, _> = &promoted.borrow();
*super::do_mir_borrowck(&infcx, input_body, promoted, Some(options)).1.unwrap()
*super::do_mir_borrowck(tcx, input_body, promoted, Some(options)).1.unwrap()
}

View File

@ -23,9 +23,8 @@
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::*;
use rustc_middle::query::Providers;
@ -123,9 +122,8 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
return tcx.arena.alloc(result);
}
let infcx = tcx.infer_ctxt().with_opaque_type_inference(def).build();
let promoted: &IndexSlice<_, _> = &promoted.borrow();
let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, None).0;
let opt_closure_req = do_mir_borrowck(tcx, input_body, promoted, None).0;
debug!("mir_borrowck done");
tcx.arena.alloc(opt_closure_req)
@ -136,18 +134,15 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
/// Use `consumer_options: None` for the default behavior of returning
/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
/// to the given [`ConsumerOptions`].
#[instrument(skip(infcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
#[instrument(skip(tcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
fn do_mir_borrowck<'tcx>(
infcx: &InferCtxt<'tcx>,
tcx: TyCtxt<'tcx>,
input_body: &Body<'tcx>,
input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
consumer_options: Option<ConsumerOptions>,
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
let def = input_body.source.def_id().expect_local();
debug!(?def);
let tcx = infcx.tcx;
let infcx = BorrowckInferCtxt::new(infcx);
let infcx = BorrowckInferCtxt::new(tcx, def);
let param_env = tcx.param_env(def);
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
@ -187,6 +182,12 @@ fn do_mir_borrowck<'tcx>(
nll::replace_regions_in_mir(&infcx, param_env, &mut body_owned, &mut promoted);
let body = &body_owned; // no further changes
// FIXME(-Znext-solver): A bit dubious that we're only registering
// predefined opaques in the typeck root.
if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
infcx.register_predefined_opaques_for_next_solver(def);
}
let location_table = LocationTable::new(body);
let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true);
@ -440,13 +441,14 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
(result, body_with_facts)
}
pub struct BorrowckInferCtxt<'cx, 'tcx> {
pub(crate) infcx: &'cx InferCtxt<'tcx>,
pub struct BorrowckInferCtxt<'tcx> {
pub(crate) infcx: InferCtxt<'tcx>,
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
}
impl<'cx, 'tcx> BorrowckInferCtxt<'cx, 'tcx> {
pub(crate) fn new(infcx: &'cx InferCtxt<'tcx>) -> Self {
impl<'tcx> BorrowckInferCtxt<'tcx> {
pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
let infcx = tcx.infer_ctxt().with_opaque_type_inference(def_id).build();
BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()) }
}
@ -492,18 +494,40 @@ pub(crate) fn next_nll_region_var<F>(
next_region
}
/// With the new solver we prepopulate the opaque type storage during
/// MIR borrowck with the hidden types from HIR typeck. This is necessary
/// to avoid ambiguities as earlier goals can rely on the hidden type
/// of an opaque which is only constrained by a later goal.
fn register_predefined_opaques_for_next_solver(&self, def_id: LocalDefId) {
let tcx = self.tcx;
// OK to use the identity arguments for each opaque type key, since
// we remap opaques from HIR typeck back to their definition params.
for data in tcx.typeck(def_id).concrete_opaque_types.iter().map(|(k, v)| (*k, *v)) {
// HIR typeck did not infer the regions of the opaque, so we instantiate
// them with fresh inference variables.
let (key, hidden_ty) = tcx.fold_regions(data, |_, _| {
self.next_nll_region_var_in_universe(
NllRegionVariableOrigin::Existential { from_forall: false },
ty::UniverseIndex::ROOT,
)
});
self.inject_new_hidden_type_unchecked(key, hidden_ty);
}
}
}
impl<'cx, 'tcx> Deref for BorrowckInferCtxt<'cx, 'tcx> {
impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
type Target = InferCtxt<'tcx>;
fn deref(&self) -> &'cx Self::Target {
self.infcx
fn deref(&self) -> &Self::Target {
&self.infcx
}
}
struct MirBorrowckCtxt<'cx, 'tcx> {
infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
infcx: &'cx BorrowckInferCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
body: &'cx Body<'tcx>,
move_data: &'cx MoveData<'tcx>,

View File

@ -51,7 +51,7 @@ pub(crate) struct NllOutput<'tcx> {
/// `compute_regions`.
#[instrument(skip(infcx, param_env, body, promoted), level = "debug")]
pub(crate) fn replace_regions_in_mir<'tcx>(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
body: &mut Body<'tcx>,
promoted: &mut IndexSlice<Promoted, Body<'tcx>>,
@ -75,7 +75,7 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
///
/// This may result in errors being reported.
pub(crate) fn compute_regions<'cx, 'tcx>(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
universal_regions: UniversalRegions<'tcx>,
body: &Body<'tcx>,
promoted: &IndexSlice<Promoted, Body<'tcx>>,
@ -202,7 +202,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
}
pub(super) fn dump_mir_results<'tcx>(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
@ -254,7 +254,7 @@ pub(super) fn dump_mir_results<'tcx>(
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
pub(super) fn dump_annotation<'tcx>(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,

View File

@ -250,10 +250,7 @@ pub enum ExtraConstraintInfo {
}
#[instrument(skip(infcx, sccs), level = "debug")]
fn sccs_info<'cx, 'tcx>(
infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
) {
fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>) {
use crate::renumber::RegionCtxt;
let var_to_origin = infcx.reg_var_to_origin.borrow();
@ -322,8 +319,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
///
/// The `outlives_constraints` and `type_tests` are an initial set
/// of constraints produced by the MIR type check.
pub(crate) fn new<'cx>(
_infcx: &BorrowckInferCtxt<'cx, 'tcx>,
pub(crate) fn new(
_infcx: &BorrowckInferCtxt<'tcx>,
var_infos: VarInfos,
universal_regions: Rc<UniversalRegions<'tcx>>,
placeholder_indices: Rc<PlaceholderIndices>,

View File

@ -11,7 +11,7 @@
/// inference variables, returning the number of variables created.
#[instrument(skip(infcx, body, promoted), level = "debug")]
pub fn renumber_mir<'tcx>(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
body: &mut Body<'tcx>,
promoted: &mut IndexSlice<Promoted, Body<'tcx>>,
) {
@ -57,7 +57,7 @@ pub(crate) fn preference_value(self) -> usize {
}
struct RegionRenumberer<'a, 'tcx> {
infcx: &'a BorrowckInferCtxt<'a, 'tcx>,
infcx: &'a BorrowckInferCtxt<'tcx>,
}
impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> {

View File

@ -24,7 +24,6 @@
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::visit::TypeVisitableExt;
@ -122,7 +121,7 @@ macro_rules! span_mirbug_and_err {
/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
/// - `elements` -- MIR region map
pub(crate) fn type_check<'mir, 'tcx>(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
body: &Body<'tcx>,
promoted: &IndexSlice<Promoted, Body<'tcx>>,
@ -865,7 +864,7 @@ fn field_ty(
/// way, it accrues region constraints -- these can later be used by
/// NLL region checking.
struct TypeChecker<'a, 'tcx> {
infcx: &'a BorrowckInferCtxt<'a, 'tcx>,
infcx: &'a BorrowckInferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
last_span: Span,
body: &'a Body<'tcx>,
@ -1020,7 +1019,7 @@ pub fn span(&self, body: &Body<'_>) -> Span {
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
fn new(
infcx: &'a BorrowckInferCtxt<'a, 'tcx>,
infcx: &'a BorrowckInferCtxt<'tcx>,
body: &'a Body<'tcx>,
param_env: ty::ParamEnv<'tcx>,
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
@ -1028,7 +1027,7 @@ fn new(
implicit_region_bound: ty::Region<'tcx>,
borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>,
) -> Self {
let mut checker = Self {
Self {
infcx,
last_span: body.span,
body,
@ -1039,74 +1038,6 @@ fn new(
implicit_region_bound,
borrowck_context,
reported_errors: Default::default(),
};
// FIXME(-Znext-solver): A bit dubious that we're only registering
// predefined opaques in the typeck root.
if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
checker.register_predefined_opaques_for_next_solver();
}
checker
}
pub(super) fn register_predefined_opaques_for_next_solver(&mut self) {
// OK to use the identity arguments for each opaque type key, since
// we remap opaques from HIR typeck back to their definition params.
let opaques: Vec<_> = self
.infcx
.tcx
.typeck(self.body.source.def_id().expect_local())
.concrete_opaque_types
.iter()
.map(|(k, v)| (*k, *v))
.collect();
let renumbered_opaques = self.infcx.tcx.fold_regions(opaques, |_, _| {
self.infcx.next_nll_region_var_in_universe(
NllRegionVariableOrigin::Existential { from_forall: false },
ty::UniverseIndex::ROOT,
)
});
let param_env = self.param_env;
let result = self.fully_perform_op(
Locations::All(self.body.span),
ConstraintCategory::OpaqueType,
CustomTypeOp::new(
|ocx| {
let mut obligations = Vec::new();
for (opaque_type_key, hidden_ty) in renumbered_opaques {
let cause = ObligationCause::dummy();
ocx.infcx.insert_hidden_type(
opaque_type_key,
&cause,
param_env,
hidden_ty.ty,
&mut obligations,
)?;
ocx.infcx.add_item_bounds_for_hidden_type(
opaque_type_key.def_id.to_def_id(),
opaque_type_key.args,
cause,
param_env,
hidden_ty.ty,
&mut obligations,
);
}
ocx.register_obligations(obligations);
Ok(())
},
"register pre-defined opaques",
),
);
if result.is_err() {
self.infcx
.dcx()
.span_bug(self.body.span, "failed re-defining predefined opaques in mir typeck");
}
}

View File

@ -240,7 +240,7 @@ impl<'tcx> UniversalRegions<'tcx> {
/// signature. This will also compute the relationships that are
/// known between those regions.
pub fn new(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
mir_def: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
@ -411,7 +411,7 @@ pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diag<'_, ()>) {
}
struct UniversalRegionsBuilder<'cx, 'tcx> {
infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
infcx: &'cx BorrowckInferCtxt<'tcx>,
mir_def: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
}
@ -796,7 +796,7 @@ fn compute_inputs_and_output(
}
#[extension(trait InferCtxtExt<'tcx>)]
impl<'cx, 'tcx> BorrowckInferCtxt<'cx, 'tcx> {
impl<'tcx> BorrowckInferCtxt<'tcx> {
#[instrument(skip(self), level = "debug")]
fn replace_free_regions_with_nll_infer_vars<T>(
&self,

View File

@ -485,6 +485,19 @@ fn register_hidden_type(
Ok(InferOk { value: (), obligations })
}
/// Insert a hidden type into the opaque type storage, making sure
/// it hasn't previously been defined. This does not emit any
/// constraints and it's the responsibility of the caller to make
/// sure that the item bounds of the opaque are checked.
pub fn inject_new_hidden_type_unchecked(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
hidden_ty: OpaqueHiddenType<'tcx>,
) {
let prev = self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty);
assert_eq!(prev, None);
}
/// Insert a hidden type into the opaque type storage, equating it
/// with any previous entries if necessary.
///

View File

@ -236,7 +236,7 @@ pub(super) fn instantiate_and_apply_query_response(
normalization_nested_goals,
} = external_constraints.deref();
self.register_region_constraints(region_constraints);
self.register_new_opaque_types(param_env, opaque_types);
self.register_new_opaque_types(opaque_types);
(normalization_nested_goals.clone(), certainty)
}
@ -368,13 +368,10 @@ fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstr
assert!(region_constraints.member_constraints.is_empty());
}
fn register_new_opaque_types(
&mut self,
param_env: ty::ParamEnv<'tcx>,
opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)],
) {
fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)]) {
for &(key, ty) in opaque_types {
self.insert_hidden_type(key, param_env, ty).unwrap();
let hidden_ty = ty::OpaqueHiddenType { ty, span: DUMMY_SP };
self.infcx.inject_new_hidden_type_unchecked(key, hidden_ty);
}
}
}

View File

@ -248,8 +248,8 @@ fn enter_canonical<R>(
};
for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
ecx.insert_hidden_type(key, input.goal.param_env, ty)
.expect("failed to prepopulate opaque types");
let hidden_ty = ty::OpaqueHiddenType { ty, span: DUMMY_SP };
ecx.infcx.inject_new_hidden_type_unchecked(key, hidden_ty);
}
if !ecx.nested_goals.is_empty() {

View File

@ -2,6 +2,9 @@
// when there are multiple inputs. The `dyn Bar` should default to `+
// 'static`. This used to erroneously generate an error (cc #62517).
//
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)
//@ check-pass
trait Foo {

View File

@ -1,3 +1,6 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)
//@ check-pass
pub fn main() {}

View File

@ -1,3 +1,6 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)
//@ check-pass
use std::io::Write;

View File

@ -1,14 +0,0 @@
error: internal compiler error: error performing operation: query type op
--> $DIR/illegal-upcast-from-impl-opaque.rs:25:1
|
LL | fn illegal(x: &dyn Sub<Assoc = Foo>) -> &dyn Super<Assoc = i32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note:
--> $DIR/illegal-upcast-from-impl-opaque.rs:25:1
|
LL | fn illegal(x: &dyn Sub<Assoc = Foo>) -> &dyn Super<Assoc = i32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
query stack during panic:
end of query stack

View File

@ -1,29 +0,0 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@[next] failure-status: 101
//@[next] known-bug: unknown
//@[next] normalize-stderr-test "note: .*\n\n" -> ""
//@[next] normalize-stderr-test "thread 'rustc' panicked.*\n.*\n" -> ""
//@[next] normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: "
//@[next] normalize-stderr-test "delayed at .*" -> ""
//@[next] rustc-env:RUST_BACKTRACE=0
#![feature(trait_upcasting, type_alias_impl_trait)]
trait Super {
type Assoc;
}
trait Sub: Super {}
impl<T: ?Sized> Super for T {
type Assoc = i32;
}
type Foo = impl Sized;
fn illegal(x: &dyn Sub<Assoc = Foo>) -> &dyn Super<Assoc = i32> {
x //[current]~ mismatched types
}
fn main() {}

View File

@ -1,11 +1,11 @@
error[E0308]: mismatched types
--> $DIR/illegal-upcast-from-impl-opaque.rs:26:5
--> $DIR/upcast-defining-opaque.rs:21:5
|
LL | type Foo = impl Sized;
| ---------- the found opaque type
LL |
LL | fn illegal(x: &dyn Sub<Assoc = Foo>) -> &dyn Super<Assoc = i32> {
| ----------------------- expected `&dyn Super<Assoc = i32>` because of return type
LL | fn upcast(x: &dyn Sub<Assoc = Foo>) -> &dyn Super<Assoc = i32> {
| ----------------------- expected `&dyn Super<Assoc = i32>` because of return type
LL | x
| ^ expected trait `Super`, found trait `Sub`
|

View File

@ -0,0 +1,24 @@
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] check-pass
#![feature(trait_upcasting, type_alias_impl_trait)]
trait Super {
type Assoc;
}
trait Sub: Super {}
impl<T: ?Sized> Super for T {
type Assoc = i32;
}
type Foo = impl Sized;
fn upcast(x: &dyn Sub<Assoc = Foo>) -> &dyn Super<Assoc = i32> {
x //[current]~ mismatched types
}
fn main() {}