Skip to content

Commit 8fe59d0

Browse files
committed
detect additional uses of opaques after writeback
1 parent 77c81e8 commit 8fe59d0

File tree

12 files changed

+143
-70
lines changed

12 files changed

+143
-70
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;
@@ -249,9 +250,7 @@ fn typeck_with_inspect<'tcx>(
249250

250251
let typeck_results = fcx.resolve_type_vars_in_body(body);
251252

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

256255
// Consistency check our TypeckResults instance can hold all ItemLocalIds
257256
// 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+
let newly_added_opaque_types: Vec<_> =
18+
self.inner.borrow_mut().opaque_types().opaque_types_added_since(num_entries).collect();
19+
for (key, hidden_type) in newly_added_opaque_types {
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_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

+19-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>;
@@ -222,6 +225,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
222225
self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(span));
223226
}
224227

228+
type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries;
229+
fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries {
230+
self.inner.borrow_mut().opaque_types().num_entries()
231+
}
225232
fn clone_opaque_types_lookup_table(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
226233
self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect()
227234
}
@@ -233,6 +240,17 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
233240
.map(|(k, h)| (k, h.ty))
234241
.collect()
235242
}
243+
fn clone_opaque_types_added_since(
244+
&self,
245+
prev_entries: OpaqueTypeStorageEntries,
246+
) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
247+
self.inner
248+
.borrow_mut()
249+
.opaque_types()
250+
.opaque_types_added_since(prev_entries)
251+
.map(|(k, h)| (k, h.ty))
252+
.collect()
253+
}
236254

237255
fn register_hidden_type_in_storage(
238256
&self,

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/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);

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

+20-19
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,33 @@ where
2626
I: Interner,
2727
{
2828
pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T) -> T {
29-
let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self;
29+
let ProbeCtxt { ecx: outer, probe_kind, _result } = self;
3030

31-
let delegate = outer_ecx.delegate;
32-
let max_input_universe = outer_ecx.max_input_universe;
33-
let mut nested_ecx = EvalCtxt {
31+
let delegate = outer.delegate;
32+
let max_input_universe = outer.max_input_universe;
33+
let mut nested = EvalCtxt {
3434
delegate,
35-
variables: outer_ecx.variables,
36-
var_values: outer_ecx.var_values,
37-
current_goal_kind: outer_ecx.current_goal_kind,
38-
predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
35+
variables: outer.variables,
36+
var_values: outer.var_values,
37+
current_goal_kind: outer.current_goal_kind,
3938
max_input_universe,
40-
search_graph: outer_ecx.search_graph,
41-
nested_goals: outer_ecx.nested_goals.clone(),
42-
origin_span: outer_ecx.origin_span,
43-
tainted: outer_ecx.tainted,
44-
inspect: outer_ecx.inspect.take_and_enter_probe(),
39+
initial_opaque_types_storage_num_entries: outer
40+
.initial_opaque_types_storage_num_entries,
41+
search_graph: outer.search_graph,
42+
nested_goals: outer.nested_goals.clone(),
43+
origin_span: outer.origin_span,
44+
tainted: outer.tainted,
45+
inspect: outer.inspect.take_and_enter_probe(),
4546
};
46-
let r = nested_ecx.delegate.probe(|| {
47-
let r = f(&mut nested_ecx);
48-
nested_ecx.inspect.probe_final_state(delegate, max_input_universe);
47+
let r = nested.delegate.probe(|| {
48+
let r = f(&mut nested);
49+
nested.inspect.probe_final_state(delegate, max_input_universe);
4950
r
5051
});
51-
if !nested_ecx.inspect.is_noop() {
52+
if !nested.inspect.is_noop() {
5253
let probe_kind = probe_kind(&r);
53-
nested_ecx.inspect.probe_kind(probe_kind);
54-
outer_ecx.inspect = nested_ecx.inspect.finish_probe();
54+
nested.inspect.probe_kind(probe_kind);
55+
outer.inspect = nested.inspect.finish_probe();
5556
}
5657
r
5758
}

compiler/rustc_type_ir/src/infer_ctxt.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::fmt::Debug;
2+
13
use derive_where::derive_where;
24
#[cfg(feature = "nightly")]
35
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
@@ -246,12 +248,18 @@ pub trait InferCtxtLike: Sized {
246248
span: <Self::Interner as Interner>::Span,
247249
);
248250

251+
type OpaqueTypeStorageEntries: Debug + Copy + Default;
252+
fn opaque_types_storage_num_entries(&self) -> Self::OpaqueTypeStorageEntries;
249253
fn clone_opaque_types_lookup_table(
250254
&self,
251255
) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
252256
fn clone_duplicate_opaque_types(
253257
&self,
254258
) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
259+
fn clone_opaque_types_added_since(
260+
&self,
261+
prev_entries: Self::OpaqueTypeStorageEntries,
262+
) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
255263

256264
fn register_hidden_type_in_storage(
257265
&self,

0 commit comments

Comments
 (0)