1use std::collections::hash_map::Entry;
2
3use rustc_type_ir::data_structures::HashMap;
4use rustc_type_ir::inherent::*;
5use rustc_type_ir::solve::{Goal, QueryInput};
6use rustc_type_ir::{
7 self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, Flags, InferCtxtLike,
8 Interner, PlaceholderConst, PlaceholderType, Region, TypeFlags, TypeFoldable, TypeFolder,
9 TypeSuperFoldable, TypeVisitableExt,
10};
11use thin_vec::ThinVec;
12
13use crate::delegate::SolverDelegate;
14
15const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits(
17 TypeFlags::HAS_INFER.bits()
18 | TypeFlags::HAS_PLACEHOLDER.bits()
19 | TypeFlags::HAS_PARAM.bits()
20 | TypeFlags::HAS_FREE_REGIONS.bits()
21 | TypeFlags::HAS_RE_ERASED.bits(),
22)
23.unwrap();
24
25#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CanonicalizeInputKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
CanonicalizeInputKind::ParamEnv => "ParamEnv",
CanonicalizeInputKind::Predicate => "Predicate",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for CanonicalizeInputKind {
#[inline]
fn clone(&self) -> CanonicalizeInputKind { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CanonicalizeInputKind { }Copy)]
26enum CanonicalizeInputKind {
27 ParamEnv,
31 Predicate,
33}
34
35#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CanonicalizeMode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
CanonicalizeMode::Input(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Input",
&__self_0),
CanonicalizeMode::Response { max_input_universe: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"Response", "max_input_universe", &__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for CanonicalizeMode {
#[inline]
fn clone(&self) -> CanonicalizeMode {
let _: ::core::clone::AssertParamIsClone<CanonicalizeInputKind>;
let _: ::core::clone::AssertParamIsClone<ty::UniverseIndex>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CanonicalizeMode { }Copy)]
41enum CanonicalizeMode {
42 Input(CanonicalizeInputKind),
43 Response {
51 max_input_universe: ty::UniverseIndex,
61 },
62}
63
64pub(super) struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
65 delegate: &'a D,
66
67 canonicalize_mode: CanonicalizeMode,
69
70 variables: ThinVec<I::GenericArg>,
72 var_kinds: Vec<CanonicalVarKind<I>>,
73 variable_lookup_table: HashMap<I::GenericArg, usize>,
74 sub_root_lookup_table: HashMap<ty::TyVid, usize>,
81
82 cache: HashMap<I::Ty, I::Ty>,
85}
86
87impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
88 fn new(delegate: &'a D, canonicalize_mode: CanonicalizeMode) -> Self {
89 Canonicalizer {
90 delegate,
91 canonicalize_mode,
92 variables: Default::default(),
93 variable_lookup_table: Default::default(),
94 sub_root_lookup_table: Default::default(),
95 var_kinds: Default::default(),
96 cache: Default::default(),
97 }
98 }
99
100 pub(super) fn canonicalize_response<T: TypeFoldable<I>>(
101 delegate: &'a D,
102 max_input_universe: ty::UniverseIndex,
103 value: T,
104 ) -> ty::Canonical<I, T> {
105 let mut canonicalizer =
106 Canonicalizer::new(delegate, CanonicalizeMode::Response { max_input_universe });
107 let value = if value.has_type_flags(NEEDS_CANONICAL) {
108 value.fold_with(&mut canonicalizer)
109 } else {
110 value
111 };
112 if true {
if !!value.has_infer() {
{
::core::panicking::panic_fmt(format_args!("unexpected infer in {0:?}",
value));
}
};
};debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
113 if true {
if !!value.has_placeholders() {
{
::core::panicking::panic_fmt(format_args!("unexpected placeholders in {0:?}",
value));
}
};
};debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
114 let (max_universe, _variables, var_kinds) = canonicalizer.finalize();
115 Canonical { max_universe, var_kinds, value }
116 }
117
118 fn canonicalize_param_env(delegate: &'a D, param_env: I::ParamEnv) -> (I::ParamEnv, Self) {
122 if !param_env.has_type_flags(NEEDS_CANONICAL) {
123 let rest_canonicalizer = Canonicalizer::new(
124 delegate,
125 CanonicalizeMode::Input(CanonicalizeInputKind::Predicate),
126 );
127
128 return (param_env, rest_canonicalizer);
129 }
130
131 let do_env_and_make_rest = || {
134 let mut env_canonicalizer = Canonicalizer::new(
135 delegate,
136 CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
137 );
138 let param_env = param_env.fold_with(&mut env_canonicalizer);
139
140 if true {
if !env_canonicalizer.sub_root_lookup_table.is_empty() {
::core::panicking::panic("assertion failed: env_canonicalizer.sub_root_lookup_table.is_empty()")
};
};debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
146 let rest_canonicalizer = Canonicalizer {
147 canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate),
148 cache: Default::default(),
149 ..env_canonicalizer
150 };
151
152 (param_env, rest_canonicalizer)
153 };
154
155 if !param_env.has_non_region_infer() {
163 delegate.cx().with_canonical_param_env_cache(|cache| match cache.0.entry(param_env) {
164 Entry::Vacant(e) => {
165 let (param_env, rest_canonicalizer) = do_env_and_make_rest();
168 e.insert(CanonicalParamEnvCacheEntry {
169 param_env,
170 variables: rest_canonicalizer.variables.clone(),
171 var_kinds: rest_canonicalizer.var_kinds.clone(),
172 variable_lookup_table: rest_canonicalizer.variable_lookup_table.clone(),
173 });
174 (param_env, rest_canonicalizer)
175 }
176 Entry::Occupied(e) => {
177 let e = e.get();
179 let mut rest_canonicalizer = Canonicalizer::new(
180 delegate,
181 CanonicalizeMode::Input(CanonicalizeInputKind::Predicate),
182 );
183 rest_canonicalizer.variables.extend(e.variables.iter().copied());
184 rest_canonicalizer.var_kinds.clone_from(&e.var_kinds);
185 rest_canonicalizer.variable_lookup_table.clone_from(&e.variable_lookup_table);
186 (e.param_env, rest_canonicalizer)
187 }
188 })
189 } else {
190 do_env_and_make_rest()
192 }
193 }
194
195 pub(super) fn canonicalize_input<P: TypeFoldable<I>>(
204 delegate: &'a D,
205 input: QueryInput<I, P>,
206 ) -> (ThinVec<I::GenericArg>, ty::Canonical<I, QueryInput<I, P>>) {
207 let (param_env, mut rest_canonicalizer) =
210 Self::canonicalize_param_env(delegate, input.goal.param_env);
211
212 let predicate = input.goal.predicate;
213 let predicate = predicate.fold_with(&mut rest_canonicalizer);
214 let goal = Goal { param_env, predicate };
215
216 let predefined_opaques_in_body = input.predefined_opaques_in_body;
217 let predefined_opaques_in_body =
218 if predefined_opaques_in_body.has_type_flags(NEEDS_CANONICAL) {
219 predefined_opaques_in_body.fold_with(&mut rest_canonicalizer)
220 } else {
221 predefined_opaques_in_body
222 };
223
224 let value = QueryInput { goal, predefined_opaques_in_body };
225
226 if true {
if !!value.has_infer() {
{
::core::panicking::panic_fmt(format_args!("unexpected infer in {0:?}",
value));
}
};
};debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
227 if true {
if !!value.has_placeholders() {
{
::core::panicking::panic_fmt(format_args!("unexpected placeholders in {0:?}",
value));
}
};
};debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
228 let (max_universe, variables, var_kinds) = rest_canonicalizer.finalize();
229 (variables, Canonical { max_universe, var_kinds, value })
230 }
231
232 fn get_or_insert_bound_var(
233 &mut self,
234 arg: impl Into<I::GenericArg>,
235 kind: CanonicalVarKind<I>,
236 ) -> ty::BoundVar {
237 let arg = arg.into();
241 let idx = if self.variables.len() > 16 {
242 if self.variable_lookup_table.is_empty() {
243 self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..));
244 }
245
246 *self.variable_lookup_table.entry(arg).or_insert_with(|| {
247 let var = self.variables.len();
248 self.variables.push(arg);
249 self.var_kinds.push(kind);
250 var
251 })
252 } else {
253 self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
254 let var = self.variables.len();
255 self.variables.push(arg);
256 self.var_kinds.push(kind);
257 var
258 })
259 };
260
261 ty::BoundVar::from(idx)
262 }
263
264 fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar {
265 let root_vid = self.delegate.sub_unification_table_root_var(vid);
266 let idx =
267 *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len());
268 ty::BoundVar::from(idx)
269 }
270
271 fn finalize(self) -> (ty::UniverseIndex, ThinVec<I::GenericArg>, I::CanonicalVarKinds) {
272 let mut var_kinds = self.var_kinds;
273 let max_universe = match self.canonicalize_mode {
276 CanonicalizeMode::Input { .. } => {
278 if true {
if !var_kinds.iter().all(|var| var.universe() == ty::UniverseIndex::ROOT)
{
{
::core::panicking::panic_fmt(format_args!("expected all vars to be canonicalized in root universe: {0:#?}",
var_kinds));
}
};
};debug_assert!(
279 var_kinds.iter().all(|var| var.universe() == ty::UniverseIndex::ROOT),
280 "expected all vars to be canonicalized in root universe: {var_kinds:#?}"
281 );
282 ty::UniverseIndex::ROOT
283 }
284 CanonicalizeMode::Response { max_input_universe } => {
289 for var in var_kinds.iter_mut() {
290 let uv = var.universe();
291 let new_uv = ty::UniverseIndex::from(
292 uv.index().saturating_sub(max_input_universe.index()),
293 );
294 *var = var.with_updated_universe(new_uv);
295 }
296 var_kinds
297 .iter()
298 .map(|kind| kind.universe())
299 .max()
300 .unwrap_or(ty::UniverseIndex::ROOT)
301 }
302 };
303 let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
304 (max_universe, self.variables, var_kinds)
305 }
306
307 fn inner_fold_ty(&mut self, t: I::Ty) -> I::Ty {
308 let kind = match t.kind() {
309 ty::Infer(i) => match i {
310 ty::TyVar(vid) => {
311 if true {
{
match (&self.delegate.opportunistic_resolve_ty_var(vid), &t) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("ty vid should have been resolved fully before canonicalization")));
}
}
}
};
};debug_assert_eq!(
312 self.delegate.opportunistic_resolve_ty_var(vid),
313 t,
314 "ty vid should have been resolved fully before canonicalization"
315 );
316
317 let sub_root = self.get_or_insert_sub_root(vid);
318 let ui = match self.canonicalize_mode {
319 CanonicalizeMode::Input { .. } => ty::UniverseIndex::ROOT,
320 CanonicalizeMode::Response { .. } => self
321 .delegate
322 .universe_of_ty(vid)
323 .unwrap_or_else(|| {
::core::panicking::panic_fmt(format_args!("ty var should have been resolved: {0:?}",
t));
}panic!("ty var should have been resolved: {t:?}")),
324 };
325 CanonicalVarKind::Ty { ui, sub_root }
326 }
327 ty::IntVar(vid) => {
328 if true {
{
match (&self.delegate.opportunistic_resolve_int_var(vid), &t) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("ty vid should have been resolved fully before canonicalization")));
}
}
}
};
};debug_assert_eq!(
329 self.delegate.opportunistic_resolve_int_var(vid),
330 t,
331 "ty vid should have been resolved fully before canonicalization"
332 );
333 CanonicalVarKind::Int
334 }
335 ty::FloatVar(vid) => {
336 if true {
{
match (&self.delegate.opportunistic_resolve_float_var(vid), &t) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("ty vid should have been resolved fully before canonicalization")));
}
}
}
};
};debug_assert_eq!(
337 self.delegate.opportunistic_resolve_float_var(vid),
338 t,
339 "ty vid should have been resolved fully before canonicalization"
340 );
341 CanonicalVarKind::Float
342 }
343 ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
344 {
::core::panicking::panic_fmt(format_args!("fresh vars not expected in canonicalization"));
}panic!("fresh vars not expected in canonicalization")
345 }
346 },
347 ty::Placeholder(placeholder) => match self.canonicalize_mode {
348 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
349 PlaceholderType::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
350 ),
351 CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
352 },
353 ty::Param(_) => match self.canonicalize_mode {
354 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
355 PlaceholderType::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
356 ),
357 CanonicalizeMode::Response { .. } => {
::core::panicking::panic_fmt(format_args!("param ty in response: {0:?}",
t));
}panic!("param ty in response: {t:?}"),
358 },
359 ty::Bool
360 | ty::Char
361 | ty::Int(_)
362 | ty::Uint(_)
363 | ty::Float(_)
364 | ty::Adt(_, _)
365 | ty::Foreign(_)
366 | ty::Str
367 | ty::Array(_, _)
368 | ty::Slice(_)
369 | ty::RawPtr(_, _)
370 | ty::Ref(_, _, _)
371 | ty::Pat(_, _)
372 | ty::FnDef(_, _)
373 | ty::FnPtr(..)
374 | ty::UnsafeBinder(_)
375 | ty::Dynamic(_, _)
376 | ty::Closure(..)
377 | ty::CoroutineClosure(..)
378 | ty::Coroutine(_, _)
379 | ty::CoroutineWitness(..)
380 | ty::Never
381 | ty::Tuple(_)
382 | ty::Alias(_, _)
383 | ty::Bound(_, _)
384 | ty::Error(_) => {
385 return t.super_fold_with(self);
386 }
387 };
388
389 let var = self.get_or_insert_bound_var(t, kind);
390
391 Ty::new_canonical_bound(self.cx(), var)
392 }
393}
394
395impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicalizer<'_, D, I> {
396 fn cx(&self) -> I {
397 self.delegate.cx()
398 }
399
400 fn fold_region(&mut self, r: Region<I>) -> Region<I> {
401 let kind = match r.kind() {
407 ty::ReBound(..) => return r,
408
409 ty::ReStatic => match self.canonicalize_mode {
412 CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => {
413 CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
414 ty::UniverseIndex::ROOT,
415 self.variables.len().into(),
416 ))
417 }
418 CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
419 | CanonicalizeMode::Response { .. } => return r,
420 },
421
422 ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
430 CanonicalizeMode::Input(_) => {
431 CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
432 ty::UniverseIndex::ROOT,
433 self.variables.len().into(),
434 ))
435 }
436 CanonicalizeMode::Response { .. } => return r,
437 },
438
439 ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
440 CanonicalizeMode::Input(_) => {
441 CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
442 ty::UniverseIndex::ROOT,
443 self.variables.len().into(),
444 ))
445 }
446 CanonicalizeMode::Response { .. } => {
447 {
::core::panicking::panic_fmt(format_args!("unexpected region in response: {0:?}",
r));
}panic!("unexpected region in response: {r:?}")
448 }
449 },
450
451 ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
452 CanonicalizeMode::Input(_) => {
453 CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
454 ty::UniverseIndex::ROOT,
455 self.variables.len().into(),
456 ))
457 }
458 CanonicalizeMode::Response { max_input_universe } => {
459 if placeholder.universe() != ty::UniverseIndex::ROOT
463 && max_input_universe.can_name(placeholder.universe())
464 {
465 {
::core::panicking::panic_fmt(format_args!("new placeholder in universe {0:?}: {1:?}",
max_input_universe, r));
};panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
466 }
467 CanonicalVarKind::PlaceholderRegion(placeholder)
468 }
469 },
470
471 ty::ReVar(vid) => {
472 if true {
{
match (&self.delegate.opportunistic_resolve_lt_var(vid), &r) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("region vid should have been resolved fully before canonicalization")));
}
}
}
};
};debug_assert_eq!(
473 self.delegate.opportunistic_resolve_lt_var(vid),
474 r,
475 "region vid should have been resolved fully before canonicalization"
476 );
477 match self.canonicalize_mode {
478 CanonicalizeMode::Input(_) => {
479 CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
480 ty::UniverseIndex::ROOT,
481 self.variables.len().into(),
482 ))
483 }
484 CanonicalizeMode::Response { .. } => {
485 CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
486 }
487 }
488 }
489 };
490
491 let var = self.get_or_insert_bound_var(r, kind);
492
493 Region::new_canonical_bound(self.cx(), var)
494 }
495
496 fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
497 if !t.flags().intersects(NEEDS_CANONICAL) {
498 t
499 } else if let Some(&ty) = self.cache.get(&t) {
500 ty
501 } else {
502 let res = self.inner_fold_ty(t);
503 let old = self.cache.insert(t, res);
504 {
match (&old, &None) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(old, None);
505 res
506 }
507 }
508
509 fn fold_const(&mut self, c: I::Const) -> I::Const {
510 if !c.flags().intersects(NEEDS_CANONICAL) {
511 return c;
512 }
513
514 let kind = match c.kind() {
515 ty::ConstKind::Infer(i) => match i {
516 ty::InferConst::Var(vid) => {
517 if true {
{
match (&self.delegate.opportunistic_resolve_ct_var(vid), &c) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("const vid should have been resolved fully before canonicalization")));
}
}
}
};
};debug_assert_eq!(
518 self.delegate.opportunistic_resolve_ct_var(vid),
519 c,
520 "const vid should have been resolved fully before canonicalization"
521 );
522
523 match self.canonicalize_mode {
524 CanonicalizeMode::Input { .. } => {
525 CanonicalVarKind::Const(ty::UniverseIndex::ROOT)
526 }
527 CanonicalizeMode::Response { .. } => {
528 CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
529 }
530 }
531 }
532 ty::InferConst::Fresh(_) => ::core::panicking::panic("not implemented")unimplemented!(),
533 },
534 ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
535 CanonicalizeMode::Input { .. } => {
536 CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon(
537 ty::UniverseIndex::ROOT,
538 self.variables.len().into(),
539 ))
540 }
541 CanonicalizeMode::Response { .. } => {
542 CanonicalVarKind::PlaceholderConst(placeholder)
543 }
544 },
545 ty::ConstKind::Param(_) => match self.canonicalize_mode {
546 CanonicalizeMode::Input { .. } => {
547 CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon(
548 ty::UniverseIndex::ROOT,
549 self.variables.len().into(),
550 ))
551 }
552 CanonicalizeMode::Response { .. } => {
::core::panicking::panic_fmt(format_args!("param ty in response: {0:?}",
c));
}panic!("param ty in response: {c:?}"),
553 },
554 ty::ConstKind::Bound(_, _)
556 | ty::ConstKind::Alias(_, _)
557 | ty::ConstKind::Value(_)
558 | ty::ConstKind::Error(_)
559 | ty::ConstKind::Expr(_) => return c.super_fold_with(self),
560 };
561
562 let var = self.get_or_insert_bound_var(c, kind);
563
564 Const::new_canonical_bound(self.cx(), var)
565 }
566
567 fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
568 if !p.flags().intersects(NEEDS_CANONICAL) { p } else { p.super_fold_with(self) }
569 }
570
571 fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
572 match self.canonicalize_mode {
573 CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
574 | CanonicalizeMode::Response { max_input_universe: _ } => {}
575 CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => {
576 { ::core::panicking::panic_fmt(format_args!("erasing \'static in env")); }panic!("erasing 'static in env")
577 }
578 }
579 if !c.flags().intersects(NEEDS_CANONICAL) { c } else { c.super_fold_with(self) }
580 }
581}