Skip to content

Commit aace488

Browse files
authored
Rollup merge of #140641 - lcnr:opaque-type-storage-entries, r=compiler-errors
detect additional uses of opaques after writeback Based on #140607. It's a lot harder to encounter in practice than I though 😅 😁 I've still added it with the expectation that somebody will encounter it at some point. Also modifies the `EvalCtxt` to use the same impl to detect newly added opaque types. r? ``@compiler-errors``
2 parents 1521f2c + e7979ea commit aace488

File tree

14 files changed

+204
-139
lines changed

14 files changed

+204
-139
lines changed

compiler/rustc_hir_typeck/src/lib.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mod gather_locals;
3232
mod intrinsicck;
3333
mod method;
3434
mod op;
35+
mod opaque_types;
3536
mod pat;
3637
mod place_op;
3738
mod rvalue_scopes;
@@ -245,9 +246,7 @@ fn typeck_with_inspect<'tcx>(
245246

246247
let typeck_results = fcx.resolve_type_vars_in_body(body);
247248

248-
// We clone the defined opaque types during writeback in the new solver
249-
// because we have to use them during normalization.
250-
let _ = fcx.infcx.take_opaque_types();
249+
fcx.detect_opaque_types_added_during_writeback();
251250

252251
// Consistency check our TypeckResults instance can hold all ItemLocalIds
253252
// it will need to hold.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use super::FnCtxt;
2+
impl<'tcx> FnCtxt<'_, 'tcx> {
3+
/// We may in theory add further uses of an opaque after cloning the opaque
4+
/// types storage during writeback when computing the defining uses.
5+
///
6+
/// Silently ignoring them is dangerous and could result in ICE or even in
7+
/// unsoundness, so we make sure we catch such cases here. There's currently
8+
/// no known code where this actually happens, even with the new solver which
9+
/// does normalize types in writeback after cloning the opaque type storage.
10+
///
11+
/// FIXME(@lcnr): I believe this should be possible in theory and would like
12+
/// an actual test here. After playing around with this for an hour, I wasn't
13+
/// able to do anything which didn't already try to normalize the opaque before
14+
/// then, either allowing compilation to succeed or causing an ambiguity error.
15+
pub(super) fn detect_opaque_types_added_during_writeback(&self) {
16+
let num_entries = self.checked_opaque_types_storage_entries.take().unwrap();
17+
for (key, hidden_type) in
18+
self.inner.borrow_mut().opaque_types().opaque_types_added_since(num_entries)
19+
{
20+
let opaque_type_string = self.tcx.def_path_str(key.def_id);
21+
let msg = format!("unexpected cyclic definition of `{opaque_type_string}`");
22+
self.dcx().span_delayed_bug(hidden_type.span, msg);
23+
}
24+
let _ = self.take_opaque_types();
25+
}
26+
}

compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use std::cell::RefCell;
1+
use std::cell::{Cell, RefCell};
22
use std::ops::Deref;
33

44
use rustc_data_structures::unord::{UnordMap, UnordSet};
55
use rustc_hir::def_id::LocalDefId;
66
use rustc_hir::{self as hir, HirId, HirIdMap, LangItem};
7-
use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
7+
use rustc_infer::infer::{InferCtxt, InferOk, OpaqueTypeStorageEntries, TyCtxtInferExt};
88
use rustc_middle::span_bug;
99
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode};
1010
use rustc_span::Span;
@@ -37,6 +37,11 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
3737

3838
pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx, FulfillmentError<'tcx>>>>,
3939

40+
// Used to detect opaque types uses added after we've already checked them.
41+
//
42+
// See [FnCtxt::detect_opaque_types_added_during_writeback] for more details.
43+
pub(super) checked_opaque_types_storage_entries: Cell<Option<OpaqueTypeStorageEntries>>,
44+
4045
/// Some additional `Sized` obligations badly affect type inference.
4146
/// These obligations are added in a later stage of typeck.
4247
/// Removing these may also cause additional complications, see #101066.
@@ -85,12 +90,14 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
8590
let infcx =
8691
tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id));
8792
let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
93+
let fulfillment_cx = RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx));
8894

8995
TypeckRootCtxt {
90-
typeck_results,
91-
fulfillment_cx: RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx)),
9296
infcx,
97+
typeck_results,
9398
locals: RefCell::new(Default::default()),
99+
fulfillment_cx,
100+
checked_opaque_types_storage_entries: Cell::new(None),
94101
deferred_sized_obligations: RefCell::new(Vec::new()),
95102
deferred_call_resolutions: RefCell::new(Default::default()),
96103
deferred_cast_checks: RefCell::new(Vec::new()),

compiler/rustc_hir_typeck/src/writeback.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -535,13 +535,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
535535
let tcx = self.tcx();
536536
// We clone the opaques instead of stealing them here as they are still used for
537537
// normalization in the next generation trait solver.
538-
//
539-
// FIXME(-Znext-solver): Opaque types defined after this would simply get dropped
540-
// at the end of typeck. While this seems unlikely to happen in practice this
541-
// should still get fixed. Either by preventing writeback from defining new opaque
542-
// types or by using this function at the end of writeback and running it as a
543-
// fixpoint.
544538
let opaque_types = self.fcx.infcx.clone_opaque_types();
539+
let num_entries = self.fcx.inner.borrow_mut().opaque_types().num_entries();
540+
let prev = self.fcx.checked_opaque_types_storage_entries.replace(Some(num_entries));
541+
debug_assert_eq!(prev, None);
545542
for (opaque_type_key, hidden_type) in opaque_types {
546543
let hidden_type = self.resolve(hidden_type, &hidden_type.span);
547544
let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span);

compiler/rustc_infer/src/infer/context.rs

+58-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use rustc_middle::ty::relate::combine::PredicateEmittingRelation;
66
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
77
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
88

9-
use super::{BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin};
9+
use super::{
10+
BoundRegionConversionTime, InferCtxt, OpaqueTypeStorageEntries, RegionVariableOrigin,
11+
SubregionOrigin,
12+
};
1013

1114
impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
1215
type Interner = TyCtxt<'tcx>;
@@ -213,4 +216,58 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
213216
fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) {
214217
self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(span));
215218
}
219+
220+
type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries;
221+
fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries {
222+
self.inner.borrow_mut().opaque_types().num_entries()
223+
}
224+
fn clone_opaque_types_lookup_table(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
225+
self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect()
226+
}
227+
fn clone_duplicate_opaque_types(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
228+
self.inner
229+
.borrow_mut()
230+
.opaque_types()
231+
.iter_duplicate_entries()
232+
.map(|(k, h)| (k, h.ty))
233+
.collect()
234+
}
235+
fn clone_opaque_types_added_since(
236+
&self,
237+
prev_entries: OpaqueTypeStorageEntries,
238+
) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
239+
self.inner
240+
.borrow_mut()
241+
.opaque_types()
242+
.opaque_types_added_since(prev_entries)
243+
.map(|(k, h)| (k, h.ty))
244+
.collect()
245+
}
246+
247+
fn register_hidden_type_in_storage(
248+
&self,
249+
opaque_type_key: ty::OpaqueTypeKey<'tcx>,
250+
hidden_ty: Ty<'tcx>,
251+
span: Span,
252+
) -> Option<Ty<'tcx>> {
253+
self.register_hidden_type_in_storage(
254+
opaque_type_key,
255+
ty::OpaqueHiddenType { span, ty: hidden_ty },
256+
)
257+
}
258+
fn add_duplicate_opaque_type(
259+
&self,
260+
opaque_type_key: ty::OpaqueTypeKey<'tcx>,
261+
hidden_ty: Ty<'tcx>,
262+
span: Span,
263+
) {
264+
self.inner
265+
.borrow_mut()
266+
.opaque_types()
267+
.add_duplicate(opaque_type_key, ty::OpaqueHiddenType { span, ty: hidden_ty })
268+
}
269+
270+
fn reset_opaque_types(&self) {
271+
let _ = self.take_opaque_types();
272+
}
216273
}

compiler/rustc_infer/src/infer/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use free_regions::RegionRelations;
99
pub use freshen::TypeFreshener;
1010
use lexical_region_resolve::LexicalRegionResolutions;
1111
pub use lexical_region_resolve::RegionResolutionError;
12-
use opaque_types::OpaqueTypeStorage;
12+
pub use opaque_types::{OpaqueTypeStorage, OpaqueTypeStorageEntries, OpaqueTypeTable};
1313
use region_constraints::{
1414
GenericKind, RegionConstraintCollector, RegionConstraintStorage, VarInfos, VerifyBound,
1515
};

compiler/rustc_infer/src/infer/opaque_types/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::traits::{self, Obligation, PredicateObligations};
1818

1919
mod table;
2020

21-
pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable};
21+
pub use table::{OpaqueTypeStorage, OpaqueTypeStorageEntries, OpaqueTypeTable};
2222

2323
impl<'tcx> InferCtxt<'tcx> {
2424
/// This is a backwards compatibility hack to prevent breaking changes from

compiler/rustc_infer/src/infer/opaque_types/table.rs

+28
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ pub struct OpaqueTypeStorage<'tcx> {
1414
duplicate_entries: Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>,
1515
}
1616

17+
/// The number of entries in the opaque type storage at a given point.
18+
///
19+
/// Used to check that we haven't added any new opaque types after checking
20+
/// the opaque types currently in the storage.
21+
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
22+
pub struct OpaqueTypeStorageEntries {
23+
opaque_types: usize,
24+
duplicate_entries: usize,
25+
}
26+
1727
impl<'tcx> OpaqueTypeStorage<'tcx> {
1828
#[instrument(level = "debug")]
1929
pub(crate) fn remove(
@@ -49,6 +59,24 @@ impl<'tcx> OpaqueTypeStorage<'tcx> {
4959
std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries))
5060
}
5161

62+
pub fn num_entries(&self) -> OpaqueTypeStorageEntries {
63+
OpaqueTypeStorageEntries {
64+
opaque_types: self.opaque_types.len(),
65+
duplicate_entries: self.duplicate_entries.len(),
66+
}
67+
}
68+
69+
pub fn opaque_types_added_since(
70+
&self,
71+
prev_entries: OpaqueTypeStorageEntries,
72+
) -> impl Iterator<Item = (OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)> {
73+
self.opaque_types
74+
.iter()
75+
.skip(prev_entries.opaque_types)
76+
.map(|(k, v)| (*k, *v))
77+
.chain(self.duplicate_entries.iter().skip(prev_entries.duplicate_entries).copied())
78+
}
79+
5280
/// Only returns the opaque types from the lookup table. These are used
5381
/// when normalizing opaque types and have a unique key.
5482
///

compiler/rustc_next_trait_solver/src/delegate.rs

-22
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,6 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
3939
term: <Self::Interner as Interner>::Term,
4040
) -> Option<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>>;
4141

42-
fn clone_opaque_types_lookup_table(
43-
&self,
44-
) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
45-
fn clone_duplicate_opaque_types(
46-
&self,
47-
) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
48-
4942
fn make_deduplicated_outlives_constraints(
5043
&self,
5144
) -> Vec<ty::OutlivesPredicate<Self::Interner, <Self::Interner as Interner>::GenericArg>>;
@@ -64,20 +57,6 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
6457
span: <Self::Interner as Interner>::Span,
6558
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
6659
) -> <Self::Interner as Interner>::GenericArg;
67-
68-
fn register_hidden_type_in_storage(
69-
&self,
70-
opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
71-
hidden_ty: <Self::Interner as Interner>::Ty,
72-
span: <Self::Interner as Interner>::Span,
73-
) -> Option<<Self::Interner as Interner>::Ty>;
74-
fn add_duplicate_opaque_type(
75-
&self,
76-
opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
77-
hidden_ty: <Self::Interner as Interner>::Ty,
78-
span: <Self::Interner as Interner>::Span,
79-
);
80-
8160
fn add_item_bounds_for_hidden_type(
8261
&self,
8362
def_id: <Self::Interner as Interner>::DefId,
@@ -86,7 +65,6 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
8665
hidden_ty: <Self::Interner as Interner>::Ty,
8766
goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
8867
);
89-
fn reset_opaque_types(&self);
9068

9169
fn fetch_eligible_assoc_item(
9270
&self,

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -250,13 +250,7 @@ where
250250
// to the `var_values`.
251251
let opaque_types = self
252252
.delegate
253-
.clone_opaque_types_lookup_table()
254-
.into_iter()
255-
.filter(|(a, _)| {
256-
self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
257-
})
258-
.chain(self.delegate.clone_duplicate_opaque_types())
259-
.collect();
253+
.clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries);
260254

261255
ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }
262256
}

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

+23-28
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ use crate::solve::inspect::{self, ProofTreeBuilder};
2323
use crate::solve::search_graph::SearchGraph;
2424
use crate::solve::{
2525
CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind, GoalSource,
26-
HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput,
27-
QueryResult,
26+
HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult,
2827
};
2928

3029
pub(super) mod canonical;
@@ -99,8 +98,6 @@ where
9998
current_goal_kind: CurrentGoalKind,
10099
pub(super) var_values: CanonicalVarValues<I>,
101100

102-
predefined_opaques_in_body: I::PredefinedOpaques,
103-
104101
/// The highest universe index nameable by the caller.
105102
///
106103
/// When we enter a new binder inside of the query we create new universes
@@ -111,6 +108,10 @@ where
111108
/// if we have a coinductive cycle and because that's the only way we can return
112109
/// new placeholders to the caller.
113110
pub(super) max_input_universe: ty::UniverseIndex,
111+
/// The opaque types from the canonical input. We only need to return opaque types
112+
/// which have been added to the storage while evaluating this goal.
113+
pub(super) initial_opaque_types_storage_num_entries:
114+
<D::Infcx as InferCtxtLike>::OpaqueTypeStorageEntries,
114115

115116
pub(super) search_graph: &'a mut SearchGraph<D>,
116117

@@ -305,10 +306,8 @@ where
305306

306307
// Only relevant when canonicalizing the response,
307308
// which we don't do within this evaluation context.
308-
predefined_opaques_in_body: delegate
309-
.cx()
310-
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
311309
max_input_universe: ty::UniverseIndex::ROOT,
310+
initial_opaque_types_storage_num_entries: Default::default(),
312311
variables: Default::default(),
313312
var_values: CanonicalVarValues::dummy(),
314313
current_goal_kind: CurrentGoalKind::Misc,
@@ -342,25 +341,10 @@ where
342341
canonical_goal_evaluation: &mut ProofTreeBuilder<D>,
343342
f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R,
344343
) -> R {
345-
let (ref delegate, input, var_values) =
346-
SolverDelegate::build_with_canonical(cx, &canonical_input);
347-
348-
let mut ecx = EvalCtxt {
349-
delegate,
350-
variables: canonical_input.canonical.variables,
351-
var_values,
352-
current_goal_kind: CurrentGoalKind::from_query_input(cx, input),
353-
predefined_opaques_in_body: input.predefined_opaques_in_body,
354-
max_input_universe: canonical_input.canonical.max_universe,
355-
search_graph,
356-
nested_goals: Default::default(),
357-
origin_span: I::Span::dummy(),
358-
tainted: Ok(()),
359-
inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values),
360-
};
344+
let (ref delegate, input, var_values) = D::build_with_canonical(cx, &canonical_input);
361345

362346
for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
363-
let prev = ecx.delegate.register_hidden_type_in_storage(key, ty, ecx.origin_span);
347+
let prev = delegate.register_hidden_type_in_storage(key, ty, I::Span::dummy());
364348
// It may be possible that two entries in the opaque type storage end up
365349
// with the same key after resolving contained inference variables.
366350
//
@@ -373,13 +357,24 @@ where
373357
// the canonical input. This is more annoying to implement and may cause a
374358
// perf regression, so we do it inside of the query for now.
375359
if let Some(prev) = prev {
376-
debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_type_storage`");
360+
debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`");
377361
}
378362
}
379363

380-
if !ecx.nested_goals.is_empty() {
381-
panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
382-
}
364+
let initial_opaque_types_storage_num_entries = delegate.opaque_types_storage_num_entries();
365+
let mut ecx = EvalCtxt {
366+
delegate,
367+
variables: canonical_input.canonical.variables,
368+
var_values,
369+
current_goal_kind: CurrentGoalKind::from_query_input(cx, input),
370+
max_input_universe: canonical_input.canonical.max_universe,
371+
initial_opaque_types_storage_num_entries,
372+
search_graph,
373+
nested_goals: Default::default(),
374+
origin_span: I::Span::dummy(),
375+
tainted: Ok(()),
376+
inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values),
377+
};
383378

384379
let result = f(&mut ecx, input.goal);
385380
ecx.inspect.probe_final_state(ecx.delegate, ecx.max_input_universe);

0 commit comments

Comments
 (0)