Skip to content

Commit bbcdc79

Browse files
committed
Add infallible folder
1 parent 23d7523 commit bbcdc79

File tree

1 file changed

+305
-0
lines changed

1 file changed

+305
-0
lines changed

chalk-ir/src/fold.rs

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Traits for transforming bits of IR.
22
33
use crate::*;
4+
use std::convert::Infallible;
45
use std::fmt::Debug;
56

67
mod binder_impls;
@@ -20,6 +21,11 @@ pub use self::subst::Subst;
2021
/// `FallibleTypeFolder`, will reconstruct itself, invoking the folder's
2122
/// methods to transform each of the types/lifetimes embedded within.
2223
///
24+
/// As the name suggests, folds performed by `FallibleTypeFolder` can
25+
/// fail (with type `Error`); if the folder cannot fail, consider
26+
/// implementing `TypeFolder` instead (which is an infallible, but
27+
/// otherwise equivalent, trait).
28+
///
2329
/// # Usage patterns
2430
///
2531
/// ## Substituting for free variables
@@ -316,6 +322,289 @@ pub trait FallibleTypeFolder<I: Interner> {
316322
fn interner(&self) -> I;
317323
}
318324

325+
/// A "folder" is a transformer that can be used to make a copy of
326+
/// some term -- that is, some bit of IR, such as a `Goal` -- with
327+
/// certain changes applied. The idea is that it contains methods that
328+
/// let you swap types/lifetimes for new types/lifetimes; meanwhile,
329+
/// each bit of IR implements the `TypeFoldable` trait which, given a
330+
/// `TypeFolder`, will reconstruct itself, invoking the folder's methods
331+
/// to transform each of the types/lifetimes embedded within.
332+
///
333+
/// Folds performed by `TypeFolder` cannot fail. If folds might fail,
334+
/// consider implementing `FallibleTypeFolder` instead (which is a
335+
/// fallible, but otherwise equivalent, trait).
336+
///
337+
/// # Usage patterns
338+
///
339+
/// ## Substituting for free variables
340+
///
341+
/// Most of the time, though, we are not interested in adjust
342+
/// arbitrary types/lifetimes, but rather just free variables (even
343+
/// more often, just free existential variables) that appear within
344+
/// the term.
345+
///
346+
/// For this reason, the `TypeFolder` trait extends two other traits that
347+
/// contain methods that are invoked when just those particular
348+
///
349+
/// In particular, folders can intercept references to free variables
350+
/// (either existentially or universally quantified) and replace them
351+
/// with other types/lifetimes as appropriate.
352+
///
353+
/// To create a folder `F`, one never implements `TypeFolder` directly, but instead
354+
/// implements one of each of these three sub-traits:
355+
///
356+
/// - `FreeVarFolder` -- folds `BoundVar` instances that appear free
357+
/// in the term being folded (use `DefaultFreeVarFolder` to
358+
/// ignore/forbid these altogether)
359+
/// - `InferenceFolder` -- folds existential `InferenceVar` instances
360+
/// that appear in the term being folded (use
361+
/// `DefaultInferenceFolder` to ignore/forbid these altogether)
362+
/// - `PlaceholderFolder` -- folds universal `Placeholder` instances
363+
/// that appear in the term being folded (use
364+
/// `DefaultPlaceholderFolder` to ignore/forbid these altogether)
365+
///
366+
/// To **apply** a folder, use the `TypeFoldable::fold_with` method, like so
367+
///
368+
/// ```rust,ignore
369+
/// let x = x.fold_with(&mut folder, 0);
370+
/// ```
371+
pub trait TypeFolder<I: Interner>: FallibleTypeFolder<I, Error = Infallible> {
372+
/// Creates a `dyn` value from this folder. Unfortunately, this
373+
/// must be added manually to each impl of TypeFolder; it permits the
374+
/// default implements below to create a `&mut dyn TypeFolder` from
375+
/// `Self` without knowing what `Self` is (by invoking this
376+
/// method). Effectively, this limits impls of `TypeFolder` to types
377+
/// for which we are able to create a dyn value (i.e., not `[T]`
378+
/// types).
379+
fn as_dyn(&mut self) -> &mut dyn TypeFolder<I>;
380+
381+
/// Top-level callback: invoked for each `Ty<I>` that is
382+
/// encountered when folding. By default, invokes
383+
/// `super_fold_with`, which will in turn invoke the more
384+
/// specialized folding methods below, like `fold_free_var_ty`.
385+
fn fold_ty(&mut self, ty: Ty<I>, outer_binder: DebruijnIndex) -> Ty<I> {
386+
ty.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
387+
}
388+
389+
/// Top-level callback: invoked for each `Lifetime<I>` that is
390+
/// encountered when folding. By default, invokes
391+
/// `super_fold_with`, which will in turn invoke the more
392+
/// specialized folding methods below, like `fold_free_var_lifetime`.
393+
fn fold_lifetime(&mut self, lifetime: Lifetime<I>, outer_binder: DebruijnIndex) -> Lifetime<I> {
394+
lifetime.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
395+
}
396+
397+
/// Top-level callback: invoked for each `Const<I>` that is
398+
/// encountered when folding. By default, invokes
399+
/// `super_fold_with`, which will in turn invoke the more
400+
/// specialized folding methods below, like `fold_free_var_const`.
401+
fn fold_const(&mut self, constant: Const<I>, outer_binder: DebruijnIndex) -> Const<I> {
402+
constant.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
403+
}
404+
405+
/// Invoked for every program clause. By default, recursively folds the goals contents.
406+
fn fold_program_clause(
407+
&mut self,
408+
clause: ProgramClause<I>,
409+
outer_binder: DebruijnIndex,
410+
) -> ProgramClause<I> {
411+
clause.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
412+
}
413+
414+
/// Invoked for every goal. By default, recursively folds the goals contents.
415+
fn fold_goal(&mut self, goal: Goal<I>, outer_binder: DebruijnIndex) -> Goal<I> {
416+
goal.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
417+
}
418+
419+
/// If overridden to return true, then folding will panic if a
420+
/// free variable is encountered. This should be done if free
421+
/// type/lifetime variables are not expected.
422+
fn forbid_free_vars(&self) -> bool {
423+
false
424+
}
425+
426+
/// Invoked for `TyKind::BoundVar` instances that are not bound
427+
/// within the type being folded over:
428+
///
429+
/// - `depth` is the depth of the `TyKind::BoundVar`; this has
430+
/// been adjusted to account for binders in scope.
431+
/// - `binders` is the number of binders in scope.
432+
///
433+
/// This should return a type suitable for a context with
434+
/// `binders` in scope.
435+
fn fold_free_var_ty(&mut self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Ty<I> {
436+
if TypeFolder::forbid_free_vars(self) {
437+
panic!(
438+
"unexpected free variable with depth `{:?}` with outer binder {:?}",
439+
bound_var, outer_binder
440+
)
441+
} else {
442+
let bound_var = bound_var.shifted_in_from(outer_binder);
443+
TyKind::<I>::BoundVar(bound_var).intern(TypeFolder::interner(self))
444+
}
445+
}
446+
447+
/// As `fold_free_var_ty`, but for lifetimes.
448+
fn fold_free_var_lifetime(
449+
&mut self,
450+
bound_var: BoundVar,
451+
outer_binder: DebruijnIndex,
452+
) -> Lifetime<I> {
453+
if TypeFolder::forbid_free_vars(self) {
454+
panic!(
455+
"unexpected free variable with depth `{:?}` with outer binder {:?}",
456+
bound_var, outer_binder
457+
)
458+
} else {
459+
let bound_var = bound_var.shifted_in_from(outer_binder);
460+
LifetimeData::<I>::BoundVar(bound_var).intern(TypeFolder::interner(self))
461+
}
462+
}
463+
464+
/// As `fold_free_var_ty`, but for constants.
465+
fn fold_free_var_const(
466+
&mut self,
467+
ty: Ty<I>,
468+
bound_var: BoundVar,
469+
outer_binder: DebruijnIndex,
470+
) -> Const<I> {
471+
if TypeFolder::forbid_free_vars(self) {
472+
panic!(
473+
"unexpected free variable with depth `{:?}` with outer binder {:?}",
474+
bound_var, outer_binder
475+
)
476+
} else {
477+
let bound_var = bound_var.shifted_in_from(outer_binder);
478+
ConstData {
479+
ty: ty.fold_with(TypeFolder::as_dyn(self), outer_binder),
480+
value: ConstValue::<I>::BoundVar(bound_var),
481+
}
482+
.intern(TypeFolder::interner(self))
483+
}
484+
}
485+
486+
/// If overridden to return true, we will panic when a free
487+
/// placeholder type/lifetime/const is encountered.
488+
fn forbid_free_placeholders(&self) -> bool {
489+
false
490+
}
491+
492+
/// Invoked for each occurrence of a placeholder type; these are
493+
/// used when we instantiate binders universally. Returns a type
494+
/// to use instead, which should be suitably shifted to account
495+
/// for `binders`.
496+
///
497+
/// - `universe` is the universe of the `TypeName::ForAll` that was found
498+
/// - `binders` is the number of binders in scope
499+
#[allow(unused_variables)]
500+
fn fold_free_placeholder_ty(
501+
&mut self,
502+
universe: PlaceholderIndex,
503+
outer_binder: DebruijnIndex,
504+
) -> Ty<I> {
505+
if TypeFolder::forbid_free_placeholders(self) {
506+
panic!("unexpected placeholder type `{:?}`", universe)
507+
} else {
508+
universe.to_ty::<I>(TypeFolder::interner(self))
509+
}
510+
}
511+
512+
/// As with `fold_free_placeholder_ty`, but for lifetimes.
513+
#[allow(unused_variables)]
514+
fn fold_free_placeholder_lifetime(
515+
&mut self,
516+
universe: PlaceholderIndex,
517+
outer_binder: DebruijnIndex,
518+
) -> Lifetime<I> {
519+
if TypeFolder::forbid_free_placeholders(self) {
520+
panic!("unexpected placeholder lifetime `{:?}`", universe)
521+
} else {
522+
universe.to_lifetime(TypeFolder::interner(self))
523+
}
524+
}
525+
526+
/// As with `fold_free_placeholder_ty`, but for constants.
527+
#[allow(unused_variables)]
528+
fn fold_free_placeholder_const(
529+
&mut self,
530+
ty: Ty<I>,
531+
universe: PlaceholderIndex,
532+
outer_binder: DebruijnIndex,
533+
) -> Const<I> {
534+
if TypeFolder::forbid_free_placeholders(self) {
535+
panic!("unexpected placeholder const `{:?}`", universe)
536+
} else {
537+
universe.to_const(
538+
TypeFolder::interner(self),
539+
ty.fold_with(TypeFolder::as_dyn(self), outer_binder),
540+
)
541+
}
542+
}
543+
544+
/// If overridden to return true, inference variables will trigger
545+
/// panics when folded. Used when inference variables are
546+
/// unexpected.
547+
fn forbid_inference_vars(&self) -> bool {
548+
false
549+
}
550+
551+
/// Invoked for each occurrence of a inference type; these are
552+
/// used when we instantiate binders universally. Returns a type
553+
/// to use instead, which should be suitably shifted to account
554+
/// for `binders`.
555+
///
556+
/// - `universe` is the universe of the `TypeName::ForAll` that was found
557+
/// - `binders` is the number of binders in scope
558+
#[allow(unused_variables)]
559+
fn fold_inference_ty(
560+
&mut self,
561+
var: InferenceVar,
562+
kind: TyVariableKind,
563+
outer_binder: DebruijnIndex,
564+
) -> Ty<I> {
565+
if TypeFolder::forbid_inference_vars(self) {
566+
panic!("unexpected inference type `{:?}`", var)
567+
} else {
568+
var.to_ty(TypeFolder::interner(self), kind)
569+
}
570+
}
571+
572+
/// As with `fold_inference_ty`, but for lifetimes.
573+
#[allow(unused_variables)]
574+
fn fold_inference_lifetime(
575+
&mut self,
576+
var: InferenceVar,
577+
outer_binder: DebruijnIndex,
578+
) -> Lifetime<I> {
579+
if TypeFolder::forbid_inference_vars(self) {
580+
panic!("unexpected inference lifetime `'{:?}`", var)
581+
} else {
582+
var.to_lifetime(TypeFolder::interner(self))
583+
}
584+
}
585+
586+
/// As with `fold_inference_ty`, but for constants.
587+
#[allow(unused_variables)]
588+
fn fold_inference_const(
589+
&mut self,
590+
ty: Ty<I>,
591+
var: InferenceVar,
592+
outer_binder: DebruijnIndex,
593+
) -> Const<I> {
594+
if TypeFolder::forbid_inference_vars(self) {
595+
panic!("unexpected inference const `{:?}`", var)
596+
} else {
597+
var.to_const(
598+
TypeFolder::interner(self),
599+
ty.fold_with(TypeFolder::as_dyn(self), outer_binder),
600+
)
601+
}
602+
}
603+
604+
/// Gets the interner that is being folded from.
605+
fn interner(&self) -> I;
606+
}
607+
319608
/// Applies the given `TypeFolder` to a value, producing a folded result
320609
/// of type `Self::Result`. The result type is typically the same as
321610
/// the source type, but in some cases we convert from borrowed
@@ -332,6 +621,14 @@ pub trait TypeFoldable<I: Interner>: Debug + Sized {
332621
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
333622
outer_binder: DebruijnIndex,
334623
) -> Result<Self, E>;
624+
625+
/// A convenient alternative to `try_fold_with` for use with infallible
626+
/// folders. Do not override this method, to ensure coherence with
627+
/// `try_fold_with`.
628+
fn fold_with(self, folder: &mut dyn TypeFolder<I>, outer_binder: DebruijnIndex) -> Self {
629+
self.try_fold_with(FallibleTypeFolder::as_dyn(folder), outer_binder)
630+
.unwrap()
631+
}
335632
}
336633

337634
/// For types where "fold" invokes a callback on the `TypeFolder`, the
@@ -344,6 +641,14 @@ pub trait TypeSuperFoldable<I: Interner>: TypeFoldable<I> {
344641
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
345642
outer_binder: DebruijnIndex,
346643
) -> Result<Self, E>;
644+
645+
/// A convenient alternative to `try_super_fold_with` for use with
646+
/// infallible folders. Do not override this method, to ensure coherence
647+
/// with `try_super_fold_with`.
648+
fn super_fold_with(self, folder: &mut dyn TypeFolder<I>, outer_binder: DebruijnIndex) -> Self {
649+
self.try_super_fold_with(FallibleTypeFolder::as_dyn(folder), outer_binder)
650+
.unwrap()
651+
}
347652
}
348653

349654
/// "Folding" a type invokes the `try_fold_ty` method on the folder; this

0 commit comments

Comments
 (0)