summaryrefslogtreecommitdiff
path: root/dhall/src/core/value.rs
diff options
context:
space:
mode:
Diffstat (limited to 'dhall/src/core/value.rs')
-rw-r--r--dhall/src/core/value.rs842
1 files changed, 287 insertions, 555 deletions
diff --git a/dhall/src/core/value.rs b/dhall/src/core/value.rs
index 0b68bf6..3cccb1d 100644
--- a/dhall/src/core/value.rs
+++ b/dhall/src/core/value.rs
@@ -1,595 +1,327 @@
-use std::collections::HashMap;
+use std::cell::{Ref, RefCell, RefMut};
+use std::rc::Rc;
-use dhall_syntax::{
- rc, Builtin, Const, ExprF, Integer, InterpolatedTextContents, Label,
- NaiveDouble, Natural, X,
-};
+use dhall_syntax::{Builtin, Const};
-use crate::core::thunk::{Thunk, TypeThunk};
-use crate::core::var::{AlphaLabel, AlphaVar, Shift, Subst};
-use crate::phase::normalize::{
- apply_builtin, normalize_one_layer, squash_textlit, OutputSubExpr,
-};
-use crate::phase::Typed;
+use crate::core::context::TypecheckContext;
+use crate::core::valuef::ValueF;
+use crate::core::var::{AlphaVar, Shift, Subst};
+use crate::error::{TypeError, TypeMessage};
+use crate::phase::normalize::{apply_any, normalize_whnf};
+use crate::phase::typecheck::{builtin_to_value, const_to_value};
+use crate::phase::{NormalizedExpr, Typed};
-/// A semantic value. The invariants ensure this value represents a Weak-Head
-/// Normal Form (WHNF). This means that this first constructor is the first constructor of the
-/// final Normal Form (NF).
-/// This WHNF must be preserved by operations on `Value`s. In particular, `subst_shift` could break
-/// the invariants so need to be careful to reevaluate as needed.
-/// Subexpressions are Thunks, which are partially evaluated expressions that are normalized
-/// on-demand. When all the Thunks in a Value are at least in WHNF, and recursively so, then the
-/// Value is in NF. This is because WHNF ensures that we have the first constructor of the NF; so
-/// if we have the first constructor of the NF at all levels, we actually have the NF.
-/// Equality is up to alpha-equivalence (renaming of bound variables) and beta-equivalence
-/// (normalization). Equality will normalize only as needed.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum Value {
- /// Closures
- Lam(AlphaLabel, TypeThunk, Thunk),
- Pi(AlphaLabel, TypeThunk, TypeThunk),
- // Invariant: the evaluation must not be able to progress further.
- AppliedBuiltin(Builtin, Vec<Thunk>),
- /// `λ(x: a) -> Some x`
- OptionalSomeClosure(TypeThunk),
- /// `λ(x : a) -> λ(xs : List a) -> [ x ] # xs`
- /// `λ(xs : List a) -> [ x ] # xs`
- ListConsClosure(TypeThunk, Option<Thunk>),
- /// `λ(x : Natural) -> x + 1`
- NaturalSuccClosure,
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum Form {
+ /// No constraints; expression may not be normalized at all.
+ Unevaled,
+ /// Weak Head Normal Form, i.e. normalized up to the first constructor, but subexpressions may
+ /// not be normalized. This means that the first constructor of the contained ValueF is the first
+ /// constructor of the final Normal Form (NF).
+ WHNF,
+ /// Normal Form, i.e. completely normalized.
+ /// When all the Values in a ValueF are at least in WHNF, and recursively so, then the
+ /// ValueF is in NF. This is because WHNF ensures that we have the first constructor of the NF; so
+ /// if we have the first constructor of the NF at all levels, we actually have the NF.
+ NF,
+}
+use Form::{Unevaled, NF, WHNF};
- Var(AlphaVar),
- Const(Const),
- BoolLit(bool),
- NaturalLit(Natural),
- IntegerLit(Integer),
- DoubleLit(NaiveDouble),
- EmptyOptionalLit(TypeThunk),
- NEOptionalLit(Thunk),
- // EmptyListLit(t) means `[] : List t`, not `[] : t`
- EmptyListLit(TypeThunk),
- NEListLit(Vec<Thunk>),
- RecordLit(HashMap<Label, Thunk>),
- RecordType(HashMap<Label, TypeThunk>),
- UnionType(HashMap<Label, Option<TypeThunk>>),
- UnionConstructor(Label, HashMap<Label, Option<TypeThunk>>),
- UnionLit(Label, Thunk, HashMap<Label, Option<TypeThunk>>),
- // Invariant: this must not contain interpolations that are themselves TextLits, and
- // contiguous text values must be merged.
- TextLit(Vec<InterpolatedTextContents<Thunk>>),
- Equivalence(TypeThunk, TypeThunk),
- // Invariant: this must not contain a value captured by one of the variants above.
- PartialExpr(ExprF<Thunk, X>),
+/// Partially normalized value.
+/// Invariant: if `form` is `WHNF`, `value` must be in Weak Head Normal Form
+/// Invariant: if `form` is `NF`, `value` must be fully normalized
+#[derive(Debug)]
+struct ValueInternal {
+ form: Form,
+ value: ValueF,
+ /// This is None if and only if `value` is `Sort` (which doesn't have a type)
+ ty: Option<Value>,
}
-impl Value {
- pub fn into_thunk(self) -> Thunk {
- Thunk::from_value(self)
+/// Stores a possibly unevaluated value. Gets (partially) normalized on-demand,
+/// sharing computation automatically. Uses a RefCell to share computation.
+/// Can optionally store a type from typechecking to preserve type information.
+#[derive(Clone)]
+pub(crate) struct Value(Rc<RefCell<ValueInternal>>);
+
+#[derive(Copy, Clone)]
+/// Controls conversion from `Value` to `Expr`
+pub(crate) struct ToExprOptions {
+ /// Whether to convert all variables to `_`
+ pub(crate) alpha: bool,
+ /// Whether to normalize before converting
+ pub(crate) normalize: bool,
+}
+
+impl ValueInternal {
+ fn into_value(self) -> Value {
+ Value(Rc::new(RefCell::new(self)))
+ }
+ fn as_valuef(&self) -> &ValueF {
+ &self.value
}
- /// Convert the value to a fully normalized syntactic expression
- pub fn normalize_to_expr(&self) -> OutputSubExpr {
- self.normalize_to_expr_maybe_alpha(false)
- }
- /// Convert the value to a fully normalized syntactic expression. Also alpha-normalize
- /// if alpha is `true`
- pub fn normalize_to_expr_maybe_alpha(&self, alpha: bool) -> OutputSubExpr {
- // Ad-hoc macro to help construct the unapplied closures
- macro_rules! make_expr {
- (Natural) => { rc(ExprF::Builtin(Builtin::Natural)) };
- (var($var:ident)) => {
- rc(ExprF::Var(dhall_syntax::V(stringify!($var).into(), 0)))
- };
- ($var:ident) => { $var };
- (List $($rest:tt)*) => {
- rc(ExprF::App(
- rc(ExprF::Builtin(Builtin::List)),
- make_expr!($($rest)*)
- ))
- };
- (Some $($rest:tt)*) => {
- rc(ExprF::SomeLit(
- make_expr!($($rest)*)
- ))
- };
- (1 + $($rest:tt)*) => {
- rc(ExprF::BinOp(
- dhall_syntax::BinOp::NaturalPlus,
- rc(ExprF::NaturalLit(1)),
- make_expr!($($rest)*)
- ))
- };
- ([ $($head:tt)* ] # $($tail:tt)*) => {
- rc(ExprF::BinOp(
- dhall_syntax::BinOp::ListAppend,
- rc(ExprF::NEListLit(vec![make_expr!($($head)*)])),
- make_expr!($($tail)*)
- ))
- };
- (λ($var:ident : $($ty:tt)*) -> $($rest:tt)*) => {
- rc(ExprF::Pi(
- stringify!($var).into(),
- make_expr!($($ty)*),
- make_expr!($($rest)*)
- ))
- };
+ fn normalize_whnf(&mut self) {
+ take_mut::take_or_recover(
+ self,
+ // Dummy value in case the other closure panics
+ || ValueInternal {
+ form: Unevaled,
+ value: ValueF::Const(Const::Type),
+ ty: None,
+ },
+ |vint| match (&vint.form, &vint.ty) {
+ (Unevaled, Some(ty)) => ValueInternal {
+ form: WHNF,
+ value: normalize_whnf(vint.value, &ty),
+ ty: vint.ty,
+ },
+ // `value` is `Sort`
+ (Unevaled, None) => ValueInternal {
+ form: NF,
+ value: ValueF::Const(Const::Sort),
+ ty: None,
+ },
+ // Already in WHNF
+ (WHNF, _) | (NF, _) => vint,
+ },
+ )
+ }
+ fn normalize_nf(&mut self) {
+ match self.form {
+ Unevaled => {
+ self.normalize_whnf();
+ self.normalize_nf();
+ }
+ WHNF => {
+ self.value.normalize_mut();
+ self.form = NF;
+ }
+ // Already in NF
+ NF => {}
}
+ }
- match self {
- Value::Lam(x, t, e) => rc(ExprF::Lam(
- x.to_label_maybe_alpha(alpha),
- t.normalize_to_expr_maybe_alpha(alpha),
- e.normalize_to_expr_maybe_alpha(alpha),
- )),
- Value::AppliedBuiltin(b, args) => {
- let mut e = rc(ExprF::Builtin(*b));
- for v in args {
- e = rc(ExprF::App(
- e,
- v.normalize_to_expr_maybe_alpha(alpha),
- ));
- }
- e
- }
- Value::OptionalSomeClosure(n) => {
- let a = n.normalize_to_expr_maybe_alpha(alpha);
- make_expr!(λ(x: a) -> Some var(x))
- }
- Value::ListConsClosure(a, None) => {
- // Avoid accidental capture of the new `x` variable
- let a1 = a.under_binder(Label::from("x"));
- let a1 = a1.normalize_to_expr_maybe_alpha(alpha);
- let a = a.normalize_to_expr_maybe_alpha(alpha);
- make_expr!(λ(x : a) -> λ(xs : List a1) -> [ var(x) ] # var(xs))
- }
- Value::ListConsClosure(n, Some(v)) => {
- // Avoid accidental capture of the new `xs` variable
- let v = v.under_binder(Label::from("xs"));
- let v = v.normalize_to_expr_maybe_alpha(alpha);
- let a = n.normalize_to_expr_maybe_alpha(alpha);
- make_expr!(λ(xs : List a) -> [ v ] # var(xs))
- }
- Value::NaturalSuccClosure => {
- make_expr!(λ(x : Natural) -> 1 + var(x))
- }
- Value::Pi(x, t, e) => rc(ExprF::Pi(
- x.to_label_maybe_alpha(alpha),
- t.normalize_to_expr_maybe_alpha(alpha),
- e.normalize_to_expr_maybe_alpha(alpha),
- )),
- Value::Var(v) => rc(ExprF::Var(v.to_var(alpha))),
- Value::Const(c) => rc(ExprF::Const(*c)),
- Value::BoolLit(b) => rc(ExprF::BoolLit(*b)),
- Value::NaturalLit(n) => rc(ExprF::NaturalLit(*n)),
- Value::IntegerLit(n) => rc(ExprF::IntegerLit(*n)),
- Value::DoubleLit(n) => rc(ExprF::DoubleLit(*n)),
- Value::EmptyOptionalLit(n) => rc(ExprF::App(
- rc(ExprF::Builtin(Builtin::OptionalNone)),
- n.normalize_to_expr_maybe_alpha(alpha),
- )),
- Value::NEOptionalLit(n) => {
- rc(ExprF::SomeLit(n.normalize_to_expr_maybe_alpha(alpha)))
- }
- Value::EmptyListLit(n) => rc(ExprF::EmptyListLit(rc(ExprF::App(
- rc(ExprF::Builtin(Builtin::List)),
- n.normalize_to_expr_maybe_alpha(alpha),
- )))),
- Value::NEListLit(elts) => rc(ExprF::NEListLit(
- elts.iter()
- .map(|n| n.normalize_to_expr_maybe_alpha(alpha))
- .collect(),
- )),
- Value::RecordLit(kvs) => rc(ExprF::RecordLit(
- kvs.iter()
- .map(|(k, v)| {
- (k.clone(), v.normalize_to_expr_maybe_alpha(alpha))
- })
- .collect(),
- )),
- Value::RecordType(kts) => rc(ExprF::RecordType(
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.normalize_to_expr_maybe_alpha(alpha))
- })
- .collect(),
- )),
- Value::UnionType(kts) => rc(ExprF::UnionType(
- kts.iter()
- .map(|(k, v)| {
- (
- k.clone(),
- v.as_ref().map(|v| {
- v.normalize_to_expr_maybe_alpha(alpha)
- }),
- )
- })
- .collect(),
- )),
- Value::UnionConstructor(l, kts) => {
- let kts = kts
- .iter()
- .map(|(k, v)| {
- (
- k.clone(),
- v.as_ref().map(|v| {
- v.normalize_to_expr_maybe_alpha(alpha)
- }),
- )
- })
- .collect();
- rc(ExprF::Field(rc(ExprF::UnionType(kts)), l.clone()))
- }
- Value::UnionLit(l, v, kts) => rc(ExprF::App(
- Value::UnionConstructor(l.clone(), kts.clone())
- .normalize_to_expr_maybe_alpha(alpha),
- v.normalize_to_expr_maybe_alpha(alpha),
- )),
- Value::TextLit(elts) => {
- use InterpolatedTextContents::{Expr, Text};
- rc(ExprF::TextLit(
- elts.iter()
- .map(|contents| match contents {
- Expr(e) => {
- Expr(e.normalize_to_expr_maybe_alpha(alpha))
- }
- Text(s) => Text(s.clone()),
- })
- .collect(),
- ))
- }
- Value::Equivalence(x, y) => rc(ExprF::BinOp(
- dhall_syntax::BinOp::Equivalence,
- x.normalize_to_expr_maybe_alpha(alpha),
- y.normalize_to_expr_maybe_alpha(alpha),
- )),
- Value::PartialExpr(e) => {
- rc(e.map_ref_simple(|v| v.normalize_to_expr_maybe_alpha(alpha)))
+ fn get_type(&self) -> Result<&Value, TypeError> {
+ match &self.ty {
+ Some(t) => Ok(t),
+ None => {
+ Err(TypeError::new(&TypecheckContext::new(), TypeMessage::Sort))
}
}
}
+}
- // Deprecated
- pub fn normalize(&self) -> Value {
- let mut v = self.clone();
- v.normalize_mut();
- v
+impl Value {
+ fn new(value: ValueF, form: Form, ty: Value) -> Value {
+ ValueInternal {
+ form,
+ value,
+ ty: Some(ty),
+ }
+ .into_value()
+ }
+ pub(crate) fn const_sort() -> Value {
+ ValueInternal {
+ form: NF,
+ value: ValueF::Const(Const::Sort),
+ ty: None,
+ }
+ .into_value()
+ }
+ pub(crate) fn from_valuef_and_type(v: ValueF, t: Value) -> Value {
+ Value::new(v, Unevaled, t)
+ }
+ pub(crate) fn from_valuef_and_type_whnf(v: ValueF, t: Value) -> Value {
+ Value::new(v, WHNF, t)
+ }
+ pub(crate) fn from_const(c: Const) -> Self {
+ const_to_value(c)
+ }
+ pub(crate) fn from_builtin(b: Builtin) -> Self {
+ builtin_to_value(b)
}
- pub fn normalize_mut(&mut self) {
- match self {
- Value::NaturalSuccClosure
- | Value::Var(_)
- | Value::Const(_)
- | Value::BoolLit(_)
- | Value::NaturalLit(_)
- | Value::IntegerLit(_)
- | Value::DoubleLit(_) => {}
+ pub(crate) fn as_const(&self) -> Option<Const> {
+ match &*self.as_whnf() {
+ ValueF::Const(c) => Some(*c),
+ _ => None,
+ }
+ }
- Value::EmptyOptionalLit(tth)
- | Value::OptionalSomeClosure(tth)
- | Value::EmptyListLit(tth) => {
- tth.normalize_mut();
- }
+ fn as_internal(&self) -> Ref<ValueInternal> {
+ self.0.borrow()
+ }
+ fn as_internal_mut(&self) -> RefMut<ValueInternal> {
+ self.0.borrow_mut()
+ }
+ /// WARNING: The returned ValueF may be entirely unnormalized, in aprticular it may just be an
+ /// unevaled PartialExpr. You probably want to use `as_whnf`.
+ fn as_valuef(&self) -> Ref<ValueF> {
+ Ref::map(self.as_internal(), ValueInternal::as_valuef)
+ }
+ /// This is what you want if you want to pattern-match on the value.
+ /// WARNING: drop this ref before normalizing the same value or you will run into BorrowMut
+ /// panics.
+ pub(crate) fn as_whnf(&self) -> Ref<ValueF> {
+ self.normalize_whnf();
+ self.as_valuef()
+ }
- Value::NEOptionalLit(th) => {
- th.normalize_mut();
- }
- Value::Lam(_, t, e) => {
- t.normalize_mut();
- e.normalize_mut();
- }
- Value::Pi(_, t, e) => {
- t.normalize_mut();
- e.normalize_mut();
- }
- Value::AppliedBuiltin(_, args) => {
- for x in args.iter_mut() {
- x.normalize_mut();
- }
- }
- Value::ListConsClosure(t, v) => {
- t.normalize_mut();
- for x in v.iter_mut() {
- x.normalize_mut();
- }
- }
- Value::NEListLit(elts) => {
- for x in elts.iter_mut() {
- x.normalize_mut();
- }
- }
- Value::RecordLit(kvs) => {
- for x in kvs.values_mut() {
- x.normalize_mut();
- }
- }
- Value::RecordType(kvs) => {
- for x in kvs.values_mut() {
- x.normalize_mut();
- }
- }
- Value::UnionType(kts) | Value::UnionConstructor(_, kts) => {
- for x in kts.values_mut().flat_map(|opt| opt) {
- x.normalize_mut();
- }
- }
- Value::UnionLit(_, v, kts) => {
- v.normalize_mut();
- for x in kts.values_mut().flat_map(|opt| opt) {
- x.normalize_mut();
- }
- }
- Value::TextLit(elts) => {
- for x in elts.iter_mut() {
- use InterpolatedTextContents::{Expr, Text};
- match x {
- Expr(n) => n.normalize_mut(),
- Text(_) => {}
- }
- }
- }
- Value::Equivalence(x, y) => {
- x.normalize_mut();
- y.normalize_mut();
- }
- Value::PartialExpr(e) => {
- // TODO: need map_mut_simple
- e.map_ref_simple(|v| {
- v.normalize_nf();
- });
- }
+ pub(crate) fn to_expr(&self, opts: ToExprOptions) -> NormalizedExpr {
+ if opts.normalize {
+ self.normalize_whnf();
}
+ self.as_valuef().to_expr(opts)
+ }
+ pub(crate) fn to_whnf_ignore_type(&self) -> ValueF {
+ self.as_whnf().clone()
+ }
+ /// Before discarding type information, check that it matches the expected return type.
+ pub(crate) fn to_whnf_check_type(&self, ty: &Value) -> ValueF {
+ self.check_type(ty);
+ self.to_whnf_ignore_type()
+ }
+ pub(crate) fn into_typed(self) -> Typed {
+ Typed::from_value(self)
}
- /// Apply to a value
- pub fn app(self, val: Value) -> Value {
- self.app_val(val)
+ /// Mutates the contents. If no one else shares this, this avoids a RefCell lock.
+ fn mutate_internal(&mut self, f: impl FnOnce(&mut ValueInternal)) {
+ match Rc::get_mut(&mut self.0) {
+ // Mutate directly if sole owner
+ Some(refcell) => f(RefCell::get_mut(refcell)),
+ // Otherwise mutate through the refcell
+ None => f(&mut self.as_internal_mut()),
+ }
+ }
+ /// Normalizes contents to normal form; faster than `normalize_nf` if
+ /// no one else shares this.
+ pub(crate) fn normalize_mut(&mut self) {
+ self.mutate_internal(|vint| vint.normalize_nf())
}
- /// Apply to a value
- pub fn app_val(self, val: Value) -> Value {
- self.app_thunk(val.into_thunk())
+ pub(crate) fn normalize_whnf(&self) {
+ let borrow = self.as_internal();
+ match borrow.form {
+ Unevaled => {
+ drop(borrow);
+ self.as_internal_mut().normalize_whnf();
+ }
+ // Already at least in WHNF
+ WHNF | NF => {}
+ }
+ }
+ pub(crate) fn normalize_nf(&self) {
+ let borrow = self.as_internal();
+ match borrow.form {
+ Unevaled | WHNF => {
+ drop(borrow);
+ self.as_internal_mut().normalize_nf();
+ }
+ // Already in NF
+ NF => {}
+ }
}
- /// Apply to a thunk
- pub fn app_thunk(self, th: Thunk) -> Value {
- Thunk::from_value(self).app_thunk(th)
+ pub(crate) fn app(&self, v: Value) -> Value {
+ let body_t = match &*self.get_type_not_sort().as_whnf() {
+ ValueF::Pi(x, t, e) => {
+ v.check_type(t);
+ e.subst_shift(&x.into(), &v)
+ }
+ _ => unreachable!("Internal type error"),
+ };
+ Value::from_valuef_and_type_whnf(
+ apply_any(self.clone(), v, &body_t),
+ body_t,
+ )
}
- pub fn from_builtin(b: Builtin) -> Value {
- Value::AppliedBuiltin(b, vec![])
+ /// In debug mode, panic if the provided type doesn't match the value's type.
+ /// Otherwise does nothing.
+ pub(crate) fn check_type(&self, ty: &Value) {
+ debug_assert_eq!(
+ Some(ty),
+ self.get_type().ok().as_ref(),
+ "Internal type error"
+ );
+ }
+ pub(crate) fn get_type(&self) -> Result<Value, TypeError> {
+ Ok(self.as_internal().get_type()?.clone())
+ }
+ /// When we know the value isn't `Sort`, this gets the type directly
+ pub(crate) fn get_type_not_sort(&self) -> Value {
+ self.get_type()
+ .expect("Internal type error: value is `Sort` but shouldn't be")
}
}
impl Shift for Value {
fn shift(&self, delta: isize, var: &AlphaVar) -> Option<Self> {
- Some(match self {
- Value::Lam(x, t, e) => Value::Lam(
- x.clone(),
- t.shift(delta, var)?,
- e.shift(delta, &var.under_binder(x))?,
- ),
- Value::AppliedBuiltin(b, args) => Value::AppliedBuiltin(
- *b,
- args.iter()
- .map(|v| Ok(v.shift(delta, var)?))
- .collect::<Result<_, _>>()?,
- ),
- Value::NaturalSuccClosure => Value::NaturalSuccClosure,
- Value::OptionalSomeClosure(tth) => {
- Value::OptionalSomeClosure(tth.shift(delta, var)?)
- }
- Value::ListConsClosure(t, v) => Value::ListConsClosure(
- t.shift(delta, var)?,
- v.as_ref().map(|v| Ok(v.shift(delta, var)?)).transpose()?,
- ),
- Value::Pi(x, t, e) => Value::Pi(
- x.clone(),
- t.shift(delta, var)?,
- e.shift(delta, &var.under_binder(x))?,
- ),
- Value::Var(v) => Value::Var(v.shift(delta, var)?),
- Value::Const(c) => Value::Const(*c),
- Value::BoolLit(b) => Value::BoolLit(*b),
- Value::NaturalLit(n) => Value::NaturalLit(*n),
- Value::IntegerLit(n) => Value::IntegerLit(*n),
- Value::DoubleLit(n) => Value::DoubleLit(*n),
- Value::EmptyOptionalLit(tth) => {
- Value::EmptyOptionalLit(tth.shift(delta, var)?)
- }
- Value::NEOptionalLit(th) => {
- Value::NEOptionalLit(th.shift(delta, var)?)
- }
- Value::EmptyListLit(tth) => {
- Value::EmptyListLit(tth.shift(delta, var)?)
- }
- Value::NEListLit(elts) => Value::NEListLit(
- elts.iter()
- .map(|v| Ok(v.shift(delta, var)?))
- .collect::<Result<_, _>>()?,
- ),
- Value::RecordLit(kvs) => Value::RecordLit(
- kvs.iter()
- .map(|(k, v)| Ok((k.clone(), v.shift(delta, var)?)))
- .collect::<Result<_, _>>()?,
- ),
- Value::RecordType(kvs) => Value::RecordType(
- kvs.iter()
- .map(|(k, v)| Ok((k.clone(), v.shift(delta, var)?)))
- .collect::<Result<_, _>>()?,
- ),
- Value::UnionType(kts) => Value::UnionType(
- kts.iter()
- .map(|(k, v)| {
- Ok((
- k.clone(),
- v.as_ref()
- .map(|v| Ok(v.shift(delta, var)?))
- .transpose()?,
- ))
- })
- .collect::<Result<_, _>>()?,
- ),
- Value::UnionConstructor(x, kts) => Value::UnionConstructor(
- x.clone(),
- kts.iter()
- .map(|(k, v)| {
- Ok((
- k.clone(),
- v.as_ref()
- .map(|v| Ok(v.shift(delta, var)?))
- .transpose()?,
- ))
- })
- .collect::<Result<_, _>>()?,
- ),
- Value::UnionLit(x, v, kts) => Value::UnionLit(
- x.clone(),
- v.shift(delta, var)?,
- kts.iter()
- .map(|(k, v)| {
- Ok((
- k.clone(),
- v.as_ref()
- .map(|v| Ok(v.shift(delta, var)?))
- .transpose()?,
- ))
- })
- .collect::<Result<_, _>>()?,
- ),
- Value::TextLit(elts) => Value::TextLit(
- elts.iter()
- .map(|contents| {
- use InterpolatedTextContents::{Expr, Text};
- Ok(match contents {
- Expr(th) => Expr(th.shift(delta, var)?),
- Text(s) => Text(s.clone()),
- })
- })
- .collect::<Result<_, _>>()?,
- ),
- Value::Equivalence(x, y) => {
- Value::Equivalence(x.shift(delta, var)?, y.shift(delta, var)?)
- }
- Value::PartialExpr(e) => Value::PartialExpr(
- e.traverse_ref_with_special_handling_of_binders(
- |v| Ok(v.shift(delta, var)?),
- |x, v| Ok(v.shift(delta, &var.under_binder(x))?),
- |x| Ok(X::clone(x)),
- )?,
- ),
+ Some(Value(self.0.shift(delta, var)?))
+ }
+}
+
+impl Shift for ValueInternal {
+ fn shift(&self, delta: isize, var: &AlphaVar) -> Option<Self> {
+ Some(ValueInternal {
+ form: self.form,
+ value: self.value.shift(delta, var)?,
+ ty: self.ty.shift(delta, var)?,
})
}
}
-impl Subst<Typed> for Value {
- fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- match self {
- // Retry normalizing since substituting may allow progress
- Value::AppliedBuiltin(b, args) => apply_builtin(
- *b,
- args.iter().map(|v| v.subst_shift(var, val)).collect(),
- ),
- // Retry normalizing since substituting may allow progress
- Value::PartialExpr(e) => {
- normalize_one_layer(e.map_ref_with_special_handling_of_binders(
- |v| v.subst_shift(var, val),
- |x, v| {
- v.subst_shift(
- &var.under_binder(x),
- &val.under_binder(x),
- )
- },
- X::clone,
- ))
- }
- // Retry normalizing since substituting may allow progress
- Value::TextLit(elts) => {
- Value::TextLit(squash_textlit(elts.iter().map(|contents| {
- use InterpolatedTextContents::{Expr, Text};
- match contents {
- Expr(th) => Expr(th.subst_shift(var, val)),
- Text(s) => Text(s.clone()),
- }
- })))
- }
- Value::Lam(x, t, e) => Value::Lam(
- x.clone(),
- t.subst_shift(var, val),
- e.subst_shift(&var.under_binder(x), &val.under_binder(x)),
- ),
- Value::NaturalSuccClosure => Value::NaturalSuccClosure,
- Value::OptionalSomeClosure(tth) => {
- Value::OptionalSomeClosure(tth.subst_shift(var, val))
- }
- Value::ListConsClosure(t, v) => Value::ListConsClosure(
- t.subst_shift(var, val),
- v.as_ref().map(|v| v.subst_shift(var, val)),
- ),
- Value::Pi(x, t, e) => Value::Pi(
- x.clone(),
- t.subst_shift(var, val),
- e.subst_shift(&var.under_binder(x), &val.under_binder(x)),
- ),
- Value::Var(v) => match v.shift(-1, var) {
- None => val.to_value().clone(),
- Some(newvar) => Value::Var(newvar),
- },
- Value::Const(c) => Value::Const(*c),
- Value::BoolLit(b) => Value::BoolLit(*b),
- Value::NaturalLit(n) => Value::NaturalLit(*n),
- Value::IntegerLit(n) => Value::IntegerLit(*n),
- Value::DoubleLit(n) => Value::DoubleLit(*n),
- Value::EmptyOptionalLit(tth) => {
- Value::EmptyOptionalLit(tth.subst_shift(var, val))
- }
- Value::NEOptionalLit(th) => {
- Value::NEOptionalLit(th.subst_shift(var, val))
- }
- Value::EmptyListLit(tth) => {
- Value::EmptyListLit(tth.subst_shift(var, val))
+impl Subst<Value> for Value {
+ fn subst_shift(&self, var: &AlphaVar, val: &Value) -> Self {
+ match &*self.as_valuef() {
+ // If the var matches, we can just reuse the provided value instead of copying it.
+ // We also check that the types match, if in debug mode.
+ ValueF::Var(v) if v == var => {
+ if let Ok(self_ty) = self.get_type() {
+ val.check_type(&self_ty.subst_shift(var, val));
+ }
+ val.clone()
}
- Value::NEListLit(elts) => Value::NEListLit(
- elts.iter().map(|v| v.subst_shift(var, val)).collect(),
- ),
- Value::RecordLit(kvs) => Value::RecordLit(
- kvs.iter()
- .map(|(k, v)| (k.clone(), v.subst_shift(var, val)))
- .collect(),
- ),
- Value::RecordType(kvs) => Value::RecordType(
- kvs.iter()
- .map(|(k, v)| (k.clone(), v.subst_shift(var, val)))
- .collect(),
- ),
- Value::UnionType(kts) => Value::UnionType(
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
- })
- .collect(),
- ),
- Value::UnionConstructor(x, kts) => Value::UnionConstructor(
- x.clone(),
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
- })
- .collect(),
- ),
- Value::UnionLit(x, v, kts) => Value::UnionLit(
- x.clone(),
- v.subst_shift(var, val),
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
- })
- .collect(),
- ),
- Value::Equivalence(x, y) => Value::Equivalence(
- x.subst_shift(var, val),
- y.subst_shift(var, val),
- ),
+ _ => Value(self.0.subst_shift(var, val)),
+ }
+ }
+}
+
+impl Subst<Value> for ValueInternal {
+ fn subst_shift(&self, var: &AlphaVar, val: &Value) -> Self {
+ ValueInternal {
+ // The resulting value may not stay in wnhf after substitution
+ form: Unevaled,
+ value: self.value.subst_shift(var, val),
+ ty: self.ty.subst_shift(var, val),
+ }
+ }
+}
+
+// TODO: use Rc comparison to shortcut on identical pointers
+impl std::cmp::PartialEq for Value {
+ fn eq(&self, other: &Self) -> bool {
+ *self.as_whnf() == *other.as_whnf()
+ }
+}
+impl std::cmp::Eq for Value {}
+
+impl std::fmt::Debug for Value {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let vint: &ValueInternal = &self.as_internal();
+ if let ValueF::Const(c) = &vint.value {
+ write!(fmt, "{:?}", c)
+ } else {
+ let mut x = fmt.debug_struct(&format!("Value@{:?}", &vint.form));
+ x.field("value", &vint.value);
+ if let Some(ty) = vint.ty.as_ref() {
+ x.field("type", &ty);
+ } else {
+ x.field("type", &None::<()>);
+ }
+ x.finish()
}
}
}