#![allow(non_snake_case)] use std::borrow::Borrow; use std::borrow::Cow; use std::collections::BTreeMap; use std::fmt; use crate::expr::*; use crate::normalize::{ AlphaVar, NormalizationContext, Thunk, TypeThunk, Value, }; use crate::traits::DynamicType; use dhall_proc_macros as dhall; use dhall_syntax; use dhall_syntax::context::Context; use dhall_syntax::*; use self::TypeMessage::*; impl Resolved { pub fn typecheck(self) -> Result { type_of(self.0) } pub fn typecheck_with(self, ty: &Type) -> Result { let expr: SubExpr<_, _> = self.0; let ty: SubExpr<_, _> = ty.to_expr().embed_absurd().note_absurd(); type_of(expr.rewrap(ExprF::Annot(expr.clone(), ty))) } /// Pretends this expression has been typechecked. Use with care. #[allow(dead_code)] pub fn skip_typecheck(self) -> Typed { Typed::from_thunk_untyped(Thunk::new( NormalizationContext::new(), self.0, )) } } impl Normalized { fn shift(&self, delta: isize, var: &AlphaVar) -> Self { Normalized(self.0.shift(delta, var)) } pub(crate) fn to_type(self) -> Type { self.0.to_type() } } impl Type { pub(crate) fn to_normalized(&self) -> Normalized { self.0.to_normalized() } pub(crate) fn to_expr(&self) -> SubExpr { self.0.to_expr() } pub(crate) fn to_value(&self) -> Value { self.0.to_value() } pub(crate) fn get_type(&self) -> Result, TypeError> { self.0.get_type() } fn as_const(&self) -> Option { self.0.as_const() } fn internal_whnf(&self) -> Option { self.0.whnf() } pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { Type(self.0.shift(delta, var)) } pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { Type(self.0.subst_shift(var, val)) } fn const_sort() -> Self { Type(TypeInternal::Const(Const::Sort)) } fn const_kind() -> Self { Type(TypeInternal::Const(Const::Kind)) } pub(crate) fn const_type() -> Self { Type(TypeInternal::Const(Const::Type)) } } impl TypeThunk { fn to_type(&self, ctx: &TypecheckContext) -> Result { match self { TypeThunk::Type(t) => Ok(t.clone()), TypeThunk::Thunk(th) => { // TODO: rule out statically mktype(ctx, th.normalize_to_expr().embed_absurd().note_absurd()) } } } } /// A semantic type. This is partially redundant with `dhall_syntax::Expr`, on purpose. `TypeInternal` should /// be limited to syntactic expressions: either written by the user or meant to be printed. /// The rule is the following: we must _not_ construct values of type `Expr` while typechecking, /// but only construct `TypeInternal`s. #[derive(Debug, Clone)] pub(crate) enum TypeInternal { Const(Const), /// This must not contain a Const value. Typed(Box), } impl TypeInternal { fn to_typed(&self) -> Typed { match self { TypeInternal::Typed(e) => e.as_ref().clone(), TypeInternal::Const(c) => const_to_typed(*c), } } fn to_normalized(&self) -> Normalized { self.to_typed().normalize() } fn to_expr(&self) -> SubExpr { self.to_normalized().to_expr() } fn to_value(&self) -> Value { self.to_typed().to_value() } pub(crate) fn get_type(&self) -> Result, TypeError> { Ok(match self { TypeInternal::Typed(e) => e.get_type()?, TypeInternal::Const(c) => Cow::Owned(type_of_const(*c)?), }) } fn as_const(&self) -> Option { match self { TypeInternal::Const(c) => Some(*c), _ => None, } } fn whnf(&self) -> Option { match self { TypeInternal::Typed(e) => Some(e.to_value()), _ => None, } } fn shift(&self, delta: isize, var: &AlphaVar) -> Self { use TypeInternal::*; match self { Typed(e) => Typed(Box::new(e.shift(delta, var))), Const(c) => Const(*c), } } fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { use TypeInternal::*; match self { Typed(e) => Typed(Box::new(e.subst_shift(var, val))), Const(c) => Const(*c), } } } impl Eq for TypeInternal {} impl PartialEq for TypeInternal { fn eq(&self, other: &Self) -> bool { self.to_normalized() == other.to_normalized() } } #[derive(Debug, Clone)] pub(crate) enum EnvItem { Type(AlphaVar, Type), Value(Normalized), } impl EnvItem { fn shift(&self, delta: isize, var: &AlphaVar) -> Self { use EnvItem::*; match self { Type(v, e) => Type(v.shift(delta, var), e.shift(delta, var)), Value(e) => Value(e.shift(delta, var)), } } } #[derive(Debug, Clone)] pub(crate) struct TypecheckContext(pub(crate) Context); impl TypecheckContext { pub(crate) fn new() -> Self { TypecheckContext(Context::new()) } pub(crate) fn insert_type(&self, x: &Label, t: Type) -> Self { TypecheckContext( self.0.map(|_, e| e.shift(1, &x.into())).insert( x.clone(), EnvItem::Type(x.into(), t.shift(1, &x.into())), ), ) } pub(crate) fn insert_value(&self, x: &Label, t: Normalized) -> Self { TypecheckContext(self.0.insert(x.clone(), EnvItem::Value(t))) } pub(crate) fn lookup(&self, var: &V