Skip to content

Commit befcfb7

Browse files
authored
Rollup merge of rust-lang#122687 - lcnr:normalizes-to-emit-nested-goals, r=compiler-errors
`NormalizesTo`: return nested goals to caller Fixes the regression of `paperclip-core`. see https://hackmd.io/IsVAafiOTAaPIFcUxRJufw for more details. r? ``@compiler-errors``
2 parents 6614397 + 0b29b71 commit befcfb7

29 files changed

+267
-198
lines changed

compiler/rustc_middle/src/traits/solve.rs

+21-8
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,19 @@ pub struct ExternalConstraintsData<'tcx> {
164164
// FIXME: implement this.
165165
pub region_constraints: QueryRegionConstraints<'tcx>,
166166
pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
167+
pub normalization_nested_goals: NestedNormalizationGoals<'tcx>,
168+
}
169+
170+
#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)]
171+
pub struct NestedNormalizationGoals<'tcx>(pub Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>);
172+
impl<'tcx> NestedNormalizationGoals<'tcx> {
173+
pub fn empty() -> Self {
174+
NestedNormalizationGoals(vec![])
175+
}
176+
177+
pub fn is_empty(&self) -> bool {
178+
self.0.is_empty()
179+
}
167180
}
168181

169182
// FIXME: Having to clone `region_constraints` for folding feels bad and
@@ -183,21 +196,27 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
183196
.iter()
184197
.map(|opaque| opaque.try_fold_with(folder))
185198
.collect::<Result<_, F::Error>>()?,
199+
normalization_nested_goals: self
200+
.normalization_nested_goals
201+
.clone()
202+
.try_fold_with(folder)?,
186203
}))
187204
}
188205

189206
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
190207
TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData {
191208
region_constraints: self.region_constraints.clone().fold_with(folder),
192209
opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(),
210+
normalization_nested_goals: self.normalization_nested_goals.clone().fold_with(folder),
193211
})
194212
}
195213
}
196214

197215
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
198216
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
199217
try_visit!(self.region_constraints.visit_with(visitor));
200-
self.opaque_types.visit_with(visitor)
218+
try_visit!(self.opaque_types.visit_with(visitor));
219+
self.normalization_nested_goals.visit_with(visitor)
201220
}
202221
}
203222

@@ -239,7 +258,7 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
239258
///
240259
/// This is necessary as we treat nested goals different depending on
241260
/// their source.
242-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
261+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TypeVisitable, TypeFoldable)]
243262
pub enum GoalSource {
244263
Misc,
245264
/// We're proving a where-bound of an impl.
@@ -256,12 +275,6 @@ pub enum GoalSource {
256275
ImplWhereBound,
257276
}
258277

259-
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)]
260-
pub enum IsNormalizesToHack {
261-
Yes,
262-
No,
263-
}
264-
265278
/// Possible ways the given goal can be proven.
266279
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267280
pub enum CandidateSource {

compiler/rustc_middle/src/traits/solve/inspect.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
//! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
2020
2121
use super::{
22-
CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack,
23-
NoSolution, QueryInput, QueryResult,
22+
CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, NoSolution,
23+
QueryInput, QueryResult,
2424
};
2525
use crate::{infer::canonical::CanonicalVarValues, ty};
2626
use format::ProofTreeFormatter;
@@ -50,7 +50,7 @@ pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>;
5050
#[derive(Eq, PartialEq)]
5151
pub enum GoalEvaluationKind<'tcx> {
5252
Root { orig_values: Vec<ty::GenericArg<'tcx>> },
53-
Nested { is_normalizes_to_hack: IsNormalizesToHack },
53+
Nested,
5454
}
5555

5656
#[derive(Eq, PartialEq)]

compiler/rustc_middle/src/traits/solve/inspect/format.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
5555
pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result {
5656
let goal_text = match eval.kind {
5757
GoalEvaluationKind::Root { orig_values: _ } => "ROOT GOAL",
58-
GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack {
59-
IsNormalizesToHack::No => "GOAL",
60-
IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
61-
},
58+
GoalEvaluationKind::Nested => "GOAL",
6259
};
6360
write!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?;
6461
self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation))

compiler/rustc_trait_selection/src/solve/alias_relate.rs

+21-12
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
//! However, if `?fresh_var` ends up geteting equated to another type, we retry the
2222
//! `NormalizesTo` goal, at which point the opaque is actually defined.
2323
24-
use super::{EvalCtxt, GoalSource};
24+
use super::EvalCtxt;
2525
use rustc_infer::traits::query::NoSolution;
26+
use rustc_infer::traits::solve::GoalSource;
2627
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
2728
use rustc_middle::ty::{self, Ty};
2829

@@ -121,10 +122,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
121122
ty::TermKind::Const(_) => {
122123
if let Some(alias) = term.to_alias_ty(self.tcx()) {
123124
let term = self.next_term_infer_of_kind(term);
124-
self.add_goal(
125-
GoalSource::Misc,
126-
Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }),
127-
);
125+
self.add_normalizes_to_goal(Goal::new(
126+
self.tcx(),
127+
param_env,
128+
ty::NormalizesTo { alias, term },
129+
));
128130
self.try_evaluate_added_goals()?;
129131
Ok(Some(self.resolve_vars_if_possible(term)))
130132
} else {
@@ -145,18 +147,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
145147
return None;
146148
}
147149

148-
let ty::Alias(_, alias) = *ty.kind() else {
150+
let ty::Alias(kind, alias) = *ty.kind() else {
149151
return Some(ty);
150152
};
151153

152154
match self.commit_if_ok(|this| {
155+
let tcx = this.tcx();
153156
let normalized_ty = this.next_ty_infer();
154-
let normalizes_to_goal = Goal::new(
155-
this.tcx(),
156-
param_env,
157-
ty::NormalizesTo { alias, term: normalized_ty.into() },
158-
);
159-
this.add_goal(GoalSource::Misc, normalizes_to_goal);
157+
let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() };
158+
match kind {
159+
ty::AliasKind::Opaque => {
160+
// HACK: Unlike for associated types, `normalizes-to` for opaques
161+
// is currently not treated as a function. We do not erase the
162+
// expected term.
163+
this.add_goal(GoalSource::Misc, Goal::new(tcx, param_env, normalizes_to));
164+
}
165+
ty::AliasKind::Projection | ty::AliasKind::Inherent | ty::AliasKind::Weak => {
166+
this.add_normalizes_to_goal(Goal::new(tcx, param_env, normalizes_to))
167+
}
168+
}
160169
this.try_evaluate_added_goals()?;
161170
Ok(this.resolve_vars_if_possible(normalized_ty))
162171
}) {

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

+38-12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//!
1010
//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
1111
use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
12+
use crate::solve::eval_ctxt::NestedGoals;
1213
use crate::solve::{
1314
inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response,
1415
};
@@ -19,6 +20,7 @@ use rustc_infer::infer::canonical::CanonicalVarValues;
1920
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
2021
use rustc_infer::infer::resolve::EagerResolver;
2122
use rustc_infer::infer::{InferCtxt, InferOk};
23+
use rustc_infer::traits::solve::NestedNormalizationGoals;
2224
use rustc_middle::infer::canonical::Canonical;
2325
use rustc_middle::traits::query::NoSolution;
2426
use rustc_middle::traits::solve::{
@@ -28,6 +30,7 @@ use rustc_middle::traits::ObligationCause;
2830
use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
2931
use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer};
3032
use rustc_span::DUMMY_SP;
33+
use std::assert_matches::assert_matches;
3134
use std::iter;
3235
use std::ops::Deref;
3336

@@ -93,13 +96,31 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
9396
previous call to `try_evaluate_added_goals!`"
9497
);
9598

96-
let certainty = certainty.unify_with(goals_certainty);
97-
98-
let var_values = self.var_values;
99-
let external_constraints = self.compute_external_query_constraints()?;
100-
99+
// When normalizing, we've replaced the expected term with an unconstrained
100+
// inference variable. This means that we dropped information which could
101+
// have been important. We handle this by instead returning the nested goals
102+
// to the caller, where they are then handled.
103+
//
104+
// As we return all ambiguous nested goals, we can ignore the certainty returned
105+
// by `try_evaluate_added_goals()`.
106+
let (certainty, normalization_nested_goals) = if self.is_normalizes_to_goal {
107+
let NestedGoals { normalizes_to_goals, goals } = std::mem::take(&mut self.nested_goals);
108+
if cfg!(debug_assertions) {
109+
assert!(normalizes_to_goals.is_empty());
110+
if goals.is_empty() {
111+
assert_matches!(goals_certainty, Certainty::Yes);
112+
}
113+
}
114+
(certainty, NestedNormalizationGoals(goals))
115+
} else {
116+
let certainty = certainty.unify_with(goals_certainty);
117+
(certainty, NestedNormalizationGoals::empty())
118+
};
119+
120+
let external_constraints =
121+
self.compute_external_query_constraints(normalization_nested_goals)?;
101122
let (var_values, mut external_constraints) =
102-
(var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
123+
(self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
103124
// Remove any trivial region constraints once we've resolved regions
104125
external_constraints
105126
.region_constraints
@@ -146,6 +167,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
146167
#[instrument(level = "debug", skip(self), ret)]
147168
fn compute_external_query_constraints(
148169
&self,
170+
normalization_nested_goals: NestedNormalizationGoals<'tcx>,
149171
) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
150172
// We only check for leaks from universes which were entered inside
151173
// of the query.
@@ -176,7 +198,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
176198
self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
177199
});
178200

179-
Ok(ExternalConstraintsData { region_constraints, opaque_types })
201+
Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals })
180202
}
181203

182204
/// After calling a canonical query, we apply the constraints returned
@@ -185,13 +207,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
185207
/// This happens in three steps:
186208
/// - we instantiate the bound variables of the query response
187209
/// - we unify the `var_values` of the response with the `original_values`
188-
/// - we apply the `external_constraints` returned by the query
210+
/// - we apply the `external_constraints` returned by the query, returning
211+
/// the `normalization_nested_goals`
189212
pub(super) fn instantiate_and_apply_query_response(
190213
&mut self,
191214
param_env: ty::ParamEnv<'tcx>,
192215
original_values: Vec<ty::GenericArg<'tcx>>,
193216
response: CanonicalResponse<'tcx>,
194-
) -> Certainty {
217+
) -> (NestedNormalizationGoals<'tcx>, Certainty) {
195218
let instantiation = Self::compute_query_response_instantiation_values(
196219
self.infcx,
197220
&original_values,
@@ -203,11 +226,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
203226

204227
Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values);
205228

206-
let ExternalConstraintsData { region_constraints, opaque_types } =
207-
external_constraints.deref();
229+
let ExternalConstraintsData {
230+
region_constraints,
231+
opaque_types,
232+
normalization_nested_goals,
233+
} = external_constraints.deref();
208234
self.register_region_constraints(region_constraints);
209235
self.register_new_opaque_types(param_env, opaque_types);
210-
certainty
236+
(normalization_nested_goals.clone(), certainty)
211237
}
212238

213239
/// This returns the canoncial variable values to instantiate the bound variables of

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

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
1111
infcx: self.infcx,
1212
variables: self.variables,
1313
var_values: self.var_values,
14+
is_normalizes_to_goal: self.is_normalizes_to_goal,
1415
predefined_opaques_in_body: self.predefined_opaques_in_body,
1516
max_input_universe: self.max_input_universe,
1617
search_graph: self.search_graph,
@@ -25,6 +26,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
2526
infcx: _,
2627
variables: _,
2728
var_values: _,
29+
is_normalizes_to_goal: _,
2830
predefined_opaques_in_body: _,
2931
max_input_universe: _,
3032
search_graph: _,

0 commit comments

Comments
 (0)