Skip to content

Commit

Permalink
Resolve imports and alternatives outside of the recursion.
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Dec 7, 2020
1 parent 4c9b9d1 commit ff0dd53
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 130 deletions.
203 changes: 139 additions & 64 deletions dhall/src/ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,19 @@ use once_cell::sync::OnceCell;
use std::marker::PhantomData;
use std::ops::{Deref, Index};

use crate::semantics::{Import, ImportLocation};
use crate::semantics::{Import, ImportLocation, ImportNode};
use crate::syntax::Span;
use crate::Typed;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ImportId<'cx>(usize, PhantomData<&'cx ()>);
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ImportResultId<'cx>(usize, PhantomData<&'cx ()>);

/// What's stored for each `ImportId`. Allows getting and setting a result for this import.
pub struct StoredImport<'cx> {
cx: Ctxt<'cx>,
pub base_location: ImportLocation,
pub import: Import,
pub span: Span,
result: OnceCell<ImportResultId<'cx>>,
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Ctxt

/// Implementation detail. Made public for the `Index` instances.
#[derive(Default)]
pub struct CtxtS<'cx> {
imports: FrozenVec<Box<StoredImport<'cx>>>,
import_results: FrozenVec<Box<Typed<'cx>>>,
import_alternatives: FrozenVec<Box<StoredImportAlternative<'cx>>>,
import_results: FrozenVec<Box<StoredImportResult<'cx>>>,
}

/// Context for the dhall compiler. Stores various global maps.
Expand All @@ -40,33 +30,46 @@ impl Ctxt<'_> {
f(cx)
}
}
impl<'cx> Ctxt<'cx> {
/// Store an import and the location relative to which it must be resolved.
pub fn push_import(
self,
base_location: ImportLocation,
import: Import,
span: Span,
) -> ImportId<'cx> {
let stored = StoredImport {
cx: self,
base_location,
import,
span,
result: OnceCell::new(),
};
let id = self.0.imports.len();
self.0.imports.push(Box::new(stored));
ImportId(id, PhantomData)
impl<'cx> Deref for Ctxt<'cx> {
type Target = &'cx CtxtS<'cx>;
fn deref(&self) -> &&'cx CtxtS<'cx> {
&self.0
}
/// Store the result of fetching an import.
pub fn push_import_result(self, res: Typed<'cx>) -> ImportResultId<'cx> {
let id = self.0.import_results.len();
self.0.import_results.push(Box::new(res));
ImportResultId(id, PhantomData)
}
impl<'a, 'cx, T> Index<&'a T> for CtxtS<'cx>
where
Self: Index<T>,
T: Copy,
{
type Output = <Self as Index<T>>::Output;
fn index(&self, id: &'a T) -> &Self::Output {
&self[*id]
}
}

/// Empty impl, because `FrozenVec` does not implement `Debug` and I can't be bothered to do it
/// myself.
impl<'cx> std::fmt::Debug for Ctxt<'cx> {
fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
Ok(())
}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
// Imports

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ImportId<'cx>(usize, PhantomData<&'cx ()>);

/// What's stored for each `ImportId`. Allows getting and setting a result for this import.
pub struct StoredImport<'cx> {
cx: Ctxt<'cx>,
pub base_location: ImportLocation,
pub import: Import,
pub span: Span,
result: OnceCell<ImportResultId<'cx>>,
}

impl<'cx> StoredImport<'cx> {
/// Get the id of the result of fetching this import. Returns `None` if the result has not yet
/// been fetched.
Expand All @@ -79,58 +82,130 @@ impl<'cx> StoredImport<'cx> {
}
/// Get the result of fetching this import. Returns `None` if the result has not yet been
/// fetched.
pub fn get_result(&self) -> Option<&'cx Typed<'cx>> {
pub fn get_result(&self) -> Option<&'cx StoredImportResult<'cx>> {
let res = self.get_resultid()?;
Some(&self.cx[res])
}
/// Get the result of fetching this import. Panicx if the result has not yet been
/// fetched.
pub fn unwrap_result(&self) -> &'cx Typed<'cx> {
pub fn unwrap_result(&self) -> &'cx StoredImportResult<'cx> {
self.get_result()
.expect("imports should all have been resolved at this stage")
}
/// Store the result of fetching this import.
pub fn set_result(&self, res: Typed<'cx>) -> ImportResultId<'cx> {
pub fn set_result(
&self,
res: StoredImportResult<'cx>,
) -> ImportResultId<'cx> {
let res = self.cx.push_import_result(res);
self.set_resultid(res);
res
}
}

impl<'cx> Deref for Ctxt<'cx> {
type Target = &'cx CtxtS<'cx>;
fn deref(&self) -> &&'cx CtxtS<'cx> {
&self.0
impl<'cx> Ctxt<'cx> {
/// Store an import and the location relative to which it must be resolved.
pub fn push_import(
self,
base_location: ImportLocation,
import: Import,
span: Span,
) -> ImportId<'cx> {
let stored = StoredImport {
cx: self,
base_location,
import,
span,
result: OnceCell::new(),
};
let id = self.0.imports.len();
self.0.imports.push(Box::new(stored));
ImportId(id, PhantomData)
}
}

impl<'cx> Index<ImportId<'cx>> for CtxtS<'cx> {
type Output = StoredImport<'cx>;
fn index(&self, id: ImportId<'cx>) -> &StoredImport<'cx> {
&self.imports[id.0]
}
}
impl<'cx> Index<ImportResultId<'cx>> for CtxtS<'cx> {
type Output = Typed<'cx>;
fn index(&self, id: ImportResultId<'cx>) -> &Typed<'cx> {
&self.import_results[id.0]

/////////////////////////////////////////////////////////////////////////////////////////////////////
// Import alternatives

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ImportAlternativeId<'cx>(usize, PhantomData<&'cx ()>);

/// What's stored for each `ImportAlternativeId`.
pub struct StoredImportAlternative<'cx> {
pub left_imports: Box<[ImportNode<'cx>]>,
pub right_imports: Box<[ImportNode<'cx>]>,
/// `true` for left, `false` for right.
selected: OnceCell<bool>,
}

impl<'cx> StoredImportAlternative<'cx> {
/// Get which alternative got selected. `true` for left, `false` for right.
pub fn get_selected(&self) -> Option<bool> {
self.selected.get().copied()
}
/// Get which alternative got selected. `true` for left, `false` for right.
pub fn unwrap_selected(&self) -> bool {
self.get_selected()
.expect("imports should all have been resolved at this stage")
}
/// Set which alternative got selected. `true` for left, `false` for right.
pub fn set_selected(&self, selected: bool) {
let _ = self.selected.set(selected);
}
}
impl<'a, 'cx, T> Index<&'a T> for CtxtS<'cx>
where
Self: Index<T>,
T: Copy,
{
type Output = <Self as Index<T>>::Output;
fn index(&self, id: &'a T) -> &Self::Output {
&self[*id]
impl<'cx> Ctxt<'cx> {
pub fn push_import_alternative(
self,
left_imports: Box<[ImportNode<'cx>]>,
right_imports: Box<[ImportNode<'cx>]>,
) -> ImportAlternativeId<'cx> {
let stored = StoredImportAlternative {
left_imports,
right_imports,
selected: OnceCell::new(),
};
let id = self.0.import_alternatives.len();
self.0.import_alternatives.push(Box::new(stored));
ImportAlternativeId(id, PhantomData)
}
}
impl<'cx> Index<ImportAlternativeId<'cx>> for CtxtS<'cx> {
type Output = StoredImportAlternative<'cx>;
fn index(
&self,
id: ImportAlternativeId<'cx>,
) -> &StoredImportAlternative<'cx> {
&self.import_alternatives[id.0]
}
}

/// Empty impl, because `FrozenVec` does not implement `Debug` and I can't be bothered to do it
/// myself.
impl<'cx> std::fmt::Debug for Ctxt<'cx> {
fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
Ok(())
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Import results

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ImportResultId<'cx>(usize, PhantomData<&'cx ()>);

type StoredImportResult<'cx> = Typed<'cx>;

impl<'cx> Ctxt<'cx> {
/// Store the result of fetching an import.
pub fn push_import_result(
self,
res: StoredImportResult<'cx>,
) -> ImportResultId<'cx> {
let id = self.0.import_results.len();
self.0.import_results.push(Box::new(res));
ImportResultId(id, PhantomData)
}
}
impl<'cx> Index<ImportResultId<'cx>> for CtxtS<'cx> {
type Output = StoredImportResult<'cx>;
fn index(&self, id: ImportResultId<'cx>) -> &StoredImportResult<'cx> {
&self.import_results[id.0]
}
}
2 changes: 1 addition & 1 deletion dhall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::semantics::resolve::ImportLocation;
use crate::semantics::{typecheck, typecheck_with, Hir, Nir, Tir, Type};
use crate::syntax::Expr;

pub use ctxt::{Ctxt, ImportId, ImportResultId};
pub use ctxt::*;

#[derive(Debug, Clone)]
pub struct Parsed(Expr, ImportLocation);
Expand Down
8 changes: 8 additions & 0 deletions dhall/src/semantics/nze/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ pub fn normalize_hir<'cx>(env: &NzEnv<'cx>, hir: &Hir<'cx>) -> NirKind<'cx> {
let typed = env.cx()[import].unwrap_result();
normalize_hir(env, &typed.hir)
}
HirKind::ImportAlternative(alt, left, right) => {
let hir = if env.cx()[alt].unwrap_selected() {
left
} else {
right
};
normalize_hir(env, hir)
}
HirKind::Expr(ExprKind::Lam(binder, annot, body)) => {
let annot = annot.eval(env);
NirKind::LamClosure {
Expand Down
14 changes: 12 additions & 2 deletions dhall/src/semantics/resolve/hir.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::error::TypeError;
use crate::semantics::{type_with, typecheck, NameEnv, Nir, NzEnv, Tir, TyEnv};
use crate::syntax::{Expr, ExprKind, Span, V};
use crate::{Ctxt, ImportId, ToExprOptions};
use crate::{Ctxt, ImportAlternativeId, ImportId, ToExprOptions};

/// Stores an alpha-normalized variable.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand All @@ -15,8 +15,10 @@ pub enum HirKind<'cx> {
Var(AlphaVar),
/// A variable that couldn't be resolved. Detected during resolution, but causes an error during typeck.
MissingVar(V),
/// An import. It must have been resolved by the time we get to typechecking/normalization.
/// An import. It must have been resolved after resolution.
Import(ImportId<'cx>),
/// An import alternative. It must have been decided after resolution.
ImportAlternative(ImportAlternativeId<'cx>, Hir<'cx>, Hir<'cx>),
// Forbidden ExprKind variants: Var, Import, Completion
Expr(ExprKind<Hir<'cx>>),
}
Expand Down Expand Up @@ -111,6 +113,14 @@ fn hir_to_expr<'cx>(
let typed = cx[import].unwrap_result();
return hir_to_expr(cx, &typed.hir, opts, &mut NameEnv::new());
}
HirKind::ImportAlternative(alt, left, right) => {
let hir = if cx[alt].unwrap_selected() {
left
} else {
right
};
return hir_to_expr(cx, hir, opts, env);
}
HirKind::Expr(e) => {
let e = e.map_ref_maybe_binder(|l, hir| {
if let Some(l) = l {
Expand Down
Loading

0 comments on commit ff0dd53

Please sign in to comment.