Skip to content

Commit d6f714e

Browse files
authored
Rollup merge of #114355 - compiler-errors:resolve_vars_early, r=lcnr
resolve before canonicalization in new solver, ICE if unresolved Fold the values with a resolver before canonicalization instead of making it happen within canonicalization. This allows us to filter trivial region constraints from the external constraints. r? ``@lcnr``
2 parents 5ea536b + d87f4a6 commit d6f714e

File tree

6 files changed

+154
-106
lines changed

6 files changed

+154
-106
lines changed

compiler/rustc_infer/src/infer/canonical/canonicalizer.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -562,15 +562,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
562562
V: TypeFoldable<TyCtxt<'tcx>>,
563563
{
564564
let needs_canonical_flags = if canonicalize_region_mode.any() {
565-
TypeFlags::HAS_INFER |
566-
TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS`
567-
TypeFlags::HAS_TY_PLACEHOLDER |
568-
TypeFlags::HAS_CT_PLACEHOLDER
565+
TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS
569566
} else {
570-
TypeFlags::HAS_INFER
571-
| TypeFlags::HAS_RE_PLACEHOLDER
572-
| TypeFlags::HAS_TY_PLACEHOLDER
573-
| TypeFlags::HAS_CT_PLACEHOLDER
567+
TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER
574568
};
575569

576570
// Fast path: nothing that needs to be canonicalized.

compiler/rustc_middle/src/traits/solve.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
147147
}
148148

149149
/// Additional constraints returned on success.
150-
#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
150+
#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)]
151151
pub struct ExternalConstraintsData<'tcx> {
152152
// FIXME: implement this.
153153
pub region_constraints: QueryRegionConstraints<'tcx>,

compiler/rustc_middle/src/ty/visit.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,10 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
8888
self.has_type_flags(TypeFlags::HAS_INFER)
8989
}
9090
fn has_placeholders(&self) -> bool {
91-
self.has_type_flags(
92-
TypeFlags::HAS_RE_PLACEHOLDER
93-
| TypeFlags::HAS_TY_PLACEHOLDER
94-
| TypeFlags::HAS_CT_PLACEHOLDER,
95-
)
91+
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER)
9692
}
9793
fn has_non_region_placeholders(&self) -> bool {
98-
self.has_type_flags(TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER)
94+
self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER)
9995
}
10096
fn has_param(&self) -> bool {
10197
self.has_type_flags(TypeFlags::HAS_PARAM)

compiler/rustc_trait_selection/src/solve/canonicalize.rs

+35-59
Original file line numberDiff line numberDiff line change
@@ -207,23 +207,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
207207
t
208208
}
209209

210-
fn fold_region(&mut self, mut r: ty::Region<'tcx>) -> ty::Region<'tcx> {
211-
match self.canonicalize_mode {
212-
CanonicalizeMode::Input => {
213-
// Don't resolve infer vars in input, since it affects
214-
// caching and may cause trait selection bugs which rely
215-
// on regions to be equal.
216-
}
217-
CanonicalizeMode::Response { .. } => {
218-
if let ty::ReVar(vid) = *r {
219-
r = self
220-
.infcx
221-
.inner
222-
.borrow_mut()
223-
.unwrap_region_constraints()
224-
.opportunistic_resolve_var(self.infcx.tcx, vid);
225-
}
226-
}
210+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
211+
if let ty::ReVar(vid) = *r {
212+
let resolved_region = self
213+
.infcx
214+
.inner
215+
.borrow_mut()
216+
.unwrap_region_constraints()
217+
.opportunistic_resolve_var(self.infcx.tcx, vid);
218+
assert_eq!(
219+
r, resolved_region,
220+
"region var should have been resolved, {r} -> {resolved_region}"
221+
);
227222
}
228223

229224
let kind = match *r {
@@ -278,38 +273,22 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
278273
ty::Region::new_late_bound(self.interner(), self.binder_index, br)
279274
}
280275

281-
fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
276+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
282277
let kind = match *t.kind() {
283-
ty::Infer(ty::TyVar(mut vid)) => {
284-
// We need to canonicalize the *root* of our ty var.
285-
// This is so that our canonical response correctly reflects
286-
// any equated inference vars correctly!
287-
let root_vid = self.infcx.root_var(vid);
288-
if root_vid != vid {
289-
t = Ty::new_var(self.infcx.tcx, root_vid);
290-
vid = root_vid;
291-
}
292-
293-
match self.infcx.probe_ty_var(vid) {
294-
Ok(t) => return self.fold_ty(t),
295-
Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
296-
}
278+
ty::Infer(ty::TyVar(vid)) => {
279+
assert_eq!(self.infcx.root_var(vid), vid, "ty vid should have been resolved");
280+
let Err(ui) = self.infcx.probe_ty_var(vid) else {
281+
bug!("ty var should have been resolved: {t}");
282+
};
283+
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
297284
}
298285
ty::Infer(ty::IntVar(vid)) => {
299-
let nt = self.infcx.opportunistic_resolve_int_var(vid);
300-
if nt != t {
301-
return self.fold_ty(nt);
302-
} else {
303-
CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
304-
}
286+
assert_eq!(self.infcx.opportunistic_resolve_int_var(vid), t);
287+
CanonicalVarKind::Ty(CanonicalTyVarKind::Int)
305288
}
306289
ty::Infer(ty::FloatVar(vid)) => {
307-
let nt = self.infcx.opportunistic_resolve_float_var(vid);
308-
if nt != t {
309-
return self.fold_ty(nt);
310-
} else {
311-
CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
312-
}
290+
assert_eq!(self.infcx.opportunistic_resolve_float_var(vid), t);
291+
CanonicalVarKind::Ty(CanonicalTyVarKind::Float)
313292
}
314293
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
315294
bug!("fresh var during canonicalization: {t:?}")
@@ -372,22 +351,19 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
372351
Ty::new_bound(self.infcx.tcx, self.binder_index, bt)
373352
}
374353

375-
fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
354+
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
376355
let kind = match c.kind() {
377-
ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => {
378-
// We need to canonicalize the *root* of our const var.
379-
// This is so that our canonical response correctly reflects
380-
// any equated inference vars correctly!
381-
let root_vid = self.infcx.root_const_var(vid);
382-
if root_vid != vid {
383-
c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty());
384-
vid = root_vid;
385-
}
386-
387-
match self.infcx.probe_const_var(vid) {
388-
Ok(c) => return self.fold_const(c),
389-
Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
390-
}
356+
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
357+
assert_eq!(
358+
self.infcx.root_const_var(vid),
359+
vid,
360+
"const var should have been resolved"
361+
);
362+
let Err(ui) = self.infcx.probe_const_var(vid) else {
363+
bug!("const var should have been resolved");
364+
};
365+
// FIXME: we should fold this ty eventually
366+
CanonicalVarKind::Const(ui, c.ty())
391367
}
392368
ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => {
393369
bug!("fresh var during canonicalization: {c:?}")

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

+109-32
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@ use rustc_index::IndexVec;
1616
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
1717
use rustc_infer::infer::canonical::CanonicalVarValues;
1818
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
19+
use rustc_infer::infer::InferCtxt;
1920
use rustc_middle::traits::query::NoSolution;
2021
use rustc_middle::traits::solve::{
21-
ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
22+
ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
23+
};
24+
use rustc_middle::ty::{
25+
self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
26+
TypeVisitableExt,
2227
};
23-
use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
2428
use rustc_span::DUMMY_SP;
2529
use std::iter;
2630
use std::ops::Deref;
@@ -32,6 +36,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
3236
&self,
3337
goal: Goal<'tcx, T>,
3438
) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
39+
let opaque_types = self.infcx.clone_opaque_types_for_query_response();
40+
let (goal, opaque_types) =
41+
(goal, opaque_types).fold_with(&mut EagerResolver { infcx: self.infcx });
42+
3543
let mut orig_values = Default::default();
3644
let canonical_goal = Canonicalizer::canonicalize(
3745
self.infcx,
@@ -40,11 +48,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
4048
QueryInput {
4149
goal,
4250
anchor: self.infcx.defining_use_anchor,
43-
predefined_opaques_in_body: self.tcx().mk_predefined_opaques_in_body(
44-
PredefinedOpaquesData {
45-
opaque_types: self.infcx.clone_opaque_types_for_query_response(),
46-
},
47-
),
51+
predefined_opaques_in_body: self
52+
.tcx()
53+
.mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }),
4854
},
4955
);
5056
(orig_values, canonical_goal)
@@ -70,34 +76,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
7076
);
7177

7278
let certainty = certainty.unify_with(goals_certainty);
79+
if let Certainty::OVERFLOW = certainty {
80+
// If we have overflow, it's probable that we're substituting a type
81+
// into itself infinitely and any partial substitutions in the query
82+
// response are probably not useful anyways, so just return an empty
83+
// query response.
84+
//
85+
// This may prevent us from potentially useful inference, e.g.
86+
// 2 candidates, one ambiguous and one overflow, which both
87+
// have the same inference constraints.
88+
//
89+
// Changing this to retain some constraints in the future
90+
// won't be a breaking change, so this is good enough for now.
91+
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow));
92+
}
7393

74-
let response = match certainty {
75-
Certainty::Yes | Certainty::Maybe(MaybeCause::Ambiguity) => {
76-
let external_constraints = self.compute_external_query_constraints()?;
77-
Response { var_values: self.var_values, external_constraints, certainty }
78-
}
79-
Certainty::Maybe(MaybeCause::Overflow) => {
80-
// If we have overflow, it's probable that we're substituting a type
81-
// into itself infinitely and any partial substitutions in the query
82-
// response are probably not useful anyways, so just return an empty
83-
// query response.
84-
//
85-
// This may prevent us from potentially useful inference, e.g.
86-
// 2 candidates, one ambiguous and one overflow, which both
87-
// have the same inference constraints.
88-
//
89-
// Changing this to retain some constraints in the future
90-
// won't be a breaking change, so this is good enough for now.
91-
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow));
92-
}
93-
};
94+
let var_values = self.var_values;
95+
let external_constraints = self.compute_external_query_constraints()?;
96+
97+
let (var_values, mut external_constraints) =
98+
(var_values, external_constraints).fold_with(&mut EagerResolver { infcx: self.infcx });
99+
// Remove any trivial region constraints once we've resolved regions
100+
external_constraints
101+
.region_constraints
102+
.outlives
103+
.retain(|(outlives, _)| outlives.0.as_region().map_or(true, |re| re != outlives.1));
94104

95105
let canonical = Canonicalizer::canonicalize(
96106
self.infcx,
97107
CanonicalizeMode::Response { max_input_universe: self.max_input_universe },
98108
&mut Default::default(),
99-
response,
109+
Response {
110+
var_values,
111+
certainty,
112+
external_constraints: self.tcx().mk_external_constraints(external_constraints),
113+
},
100114
);
115+
101116
Ok(canonical)
102117
}
103118

@@ -143,7 +158,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
143158
/// further constrained by inference, that will be passed back in the var
144159
/// values.
145160
#[instrument(level = "debug", skip(self), ret)]
146-
fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
161+
fn compute_external_query_constraints(
162+
&self,
163+
) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
147164
// We only check for leaks from universes which were entered inside
148165
// of the query.
149166
self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
@@ -173,9 +190,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
173190
self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
174191
});
175192

176-
Ok(self
177-
.tcx()
178-
.mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
193+
Ok(ExternalConstraintsData { region_constraints, opaque_types })
179194
}
180195

181196
/// After calling a canonical query, we apply the constraints returned
@@ -333,3 +348,65 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
333348
Ok(())
334349
}
335350
}
351+
352+
/// Resolves ty, region, and const vars to their inferred values or their root vars.
353+
struct EagerResolver<'a, 'tcx> {
354+
infcx: &'a InferCtxt<'tcx>,
355+
}
356+
357+
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerResolver<'_, 'tcx> {
358+
fn interner(&self) -> TyCtxt<'tcx> {
359+
self.infcx.tcx
360+
}
361+
362+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
363+
match *t.kind() {
364+
ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) {
365+
Ok(t) => t.fold_with(self),
366+
Err(_) => Ty::new_var(self.infcx.tcx, self.infcx.root_var(vid)),
367+
},
368+
ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid),
369+
ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid),
370+
_ => {
371+
if t.has_infer() {
372+
t.super_fold_with(self)
373+
} else {
374+
t
375+
}
376+
}
377+
}
378+
}
379+
380+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
381+
match *r {
382+
ty::ReVar(vid) => self
383+
.infcx
384+
.inner
385+
.borrow_mut()
386+
.unwrap_region_constraints()
387+
.opportunistic_resolve_var(self.infcx.tcx, vid),
388+
_ => r,
389+
}
390+
}
391+
392+
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
393+
match c.kind() {
394+
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
395+
// FIXME: we need to fold the ty too, I think.
396+
match self.infcx.probe_const_var(vid) {
397+
Ok(c) => c.fold_with(self),
398+
Err(_) => {
399+
ty::Const::new_var(self.infcx.tcx, self.infcx.root_const_var(vid), c.ty())
400+
}
401+
}
402+
}
403+
_ => {
404+
if c.has_infer() {
405+
c.super_fold_with(self)
406+
} else {
407+
c
408+
}
409+
}
410+
}
411+
}
412+
}

compiler/rustc_type_ir/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ bitflags! {
216216
/// Does this have `ConstKind::Placeholder`?
217217
const HAS_CT_PLACEHOLDER = 1 << 8;
218218

219+
/// Does this have placeholders?
220+
const HAS_PLACEHOLDER = TypeFlags::HAS_TY_PLACEHOLDER.bits
221+
| TypeFlags::HAS_RE_PLACEHOLDER.bits
222+
| TypeFlags::HAS_CT_PLACEHOLDER.bits;
223+
219224
/// `true` if there are "names" of regions and so forth
220225
/// that are local to a particular fn/inferctxt
221226
const HAS_FREE_LOCAL_REGIONS = 1 << 9;

0 commit comments

Comments
 (0)