1
1
mirror of https://github.com/tweag/nickel.git synced 2024-11-10 10:46:49 +03:00

Split assume op in two operators

This commit is contained in:
Yann Hamdaoui 2021-03-08 12:51:17 +01:00
parent 34a47f1bce
commit ae54d9d108
7 changed files with 57 additions and 49 deletions

View File

@ -875,7 +875,10 @@ impl ToDiagnostic<FileId> for EvalError {
// point to the builtin implementation contract like `func` or `record`, so // point to the builtin implementation contract like `func` or `record`, so
// there's no good reason to show it. Note than even in that case, the // there's no good reason to show it. Note than even in that case, the
// information contained in the argument thunk can still be useful. // information contained in the argument thunk can still be useful.
if contract_id.map(|ctrs_id| arg_pos.src_id != ctrs_id).unwrap_or(true) { if contract_id
.map(|ctrs_id| arg_pos.src_id != ctrs_id)
.unwrap_or(true)
{
labels.push(primary(&arg_pos).with_message("applied to this expression")); labels.push(primary(&arg_pos).with_message("applied to this expression"));
} }
} }

View File

@ -54,7 +54,7 @@
use crate::error::EvalError; use crate::error::EvalError;
use crate::eval::{Closure, Environment}; use crate::eval::{Closure, Environment};
use crate::position::TermPos; use crate::position::TermPos;
use crate::term::{BinaryOp, Contract, MetaValue, RichTerm, Term, make as mk_term}; use crate::term::{make as mk_term, BinaryOp, Contract, MetaValue, RichTerm, Term};
use crate::transformations::Closurizable; use crate::transformations::Closurizable;
use std::collections::HashMap; use std::collections::HashMap;

View File

@ -226,6 +226,34 @@ fn process_unary_operation(
)) ))
} }
} }
UnaryOp::ToCtrFun() => {
match *t {
Term::Fun(..) => Ok(Closure {
body: RichTerm { term: t, pos },
env,
}),
Term::Record(..) => {
let mut new_env = Environment::new();
let closurized = RichTerm { term: t, pos }.closurize(&mut new_env, env);
// Convert the record to the function `fun l x => contract & x`.
let body = mk_fun!(
"_l",
"x",
mk_term::op2(BinaryOp::Merge(), closurized, mk_term::var("x"))
)
.with_pos(pos.into_inherited());
Ok(Closure { body, env: new_env })
}
_ => Err(EvalError::TypeError(
String::from("Function or record"),
String::from("contract application, 1st argument"),
arg_pos,
RichTerm { term: t, pos },
)),
}
}
UnaryOp::Blame() => { UnaryOp::Blame() => {
if let Term::Lbl(label) = *t { if let Term::Lbl(label) = *t {
Err(EvalError::BlameError( Err(EvalError::BlameError(
@ -888,8 +916,6 @@ fn process_binary_operation(
l.arg_pos = thunk.borrow().body.pos; l.arg_pos = thunk.borrow().body.pos;
l.arg_thunk = Some(thunk); l.arg_thunk = Some(thunk);
println!("After setting label pos: types {:?}; tag {:?}; span {:?}; arg_pos {:?}; pol {:?}", l.types, l.tag, l.span, l.arg_pos, l.polarity);
stack.push_arg( stack.push_arg(
Closure::atomic_closure(RichTerm::new(Term::Lbl(l), pos2.into_inherited())), Closure::atomic_closure(RichTerm::new(Term::Lbl(l), pos2.into_inherited())),
pos2.into_inherited(), pos2.into_inherited(),
@ -903,27 +929,9 @@ fn process_binary_operation(
}, },
env: env1, env: env1,
}), }),
Term::Record(..) => {
let mut new_env = Environment::new();
let closurized = RichTerm {
term: t1,
pos: pos1,
}
.closurize(&mut new_env, env1);
// Convert the record to the function `fun l x => contract & x`.
let body = mk_fun!(
"l",
"x",
mk_term::op2(BinaryOp::Merge(), closurized, mk_term::var("x"))
)
.with_pos(pos1.into_inherited());
Ok(Closure { body, env: new_env })
}
_ => Err(EvalError::TypeError( _ => Err(EvalError::TypeError(
String::from("Function or record"), String::from("Function"),
String::from("contract application, 1st argument"), String::from("assume, 1st argument"),
fst_pos, fst_pos,
RichTerm { RichTerm {
term: t1, term: t1,
@ -934,7 +942,7 @@ fn process_binary_operation(
} else { } else {
Err(EvalError::TypeError( Err(EvalError::TypeError(
String::from("Label"), String::from("Label"),
String::from("contract application, 2nd argument"), String::from("assume, 2nd argument"),
snd_pos, snd_pos,
RichTerm { RichTerm {
term: t2, term: t2,

View File

@ -267,9 +267,7 @@ impl Stack {
/// corresponding thunk, or `None` if the top element wasn't an argument. /// corresponding thunk, or `None` if the top element wasn't an argument.
pub fn track_arg(&mut self) -> Option<Thunk> { pub fn track_arg(&mut self) -> Option<Thunk> {
match self.0.last_mut() { match self.0.last_mut() {
Some(Marker::TrackedArg(thunk, _)) => { Some(Marker::TrackedArg(thunk, _)) => Some(thunk.clone()),
Some(thunk.clone())
}
Some(Marker::Arg(..)) => { Some(Marker::Arg(..)) => {
let (closure, pos) = self.pop_arg().unwrap(); let (closure, pos) = self.pop_arg().unwrap();
let thunk = Thunk::new(closure, IdentKind::Lam()); let thunk = Thunk::new(closure, IdentKind::Lam());

View File

@ -518,6 +518,12 @@ pub enum UnaryOp {
/// Raise a blame, which stops the execution and prints an error according to the label argument. /// Raise a blame, which stops the execution and prints an error according to the label argument.
Blame(), Blame(),
/// Convert a flat contract, specified as an expression like `#SomeContract`, to a function of
/// two arguments (a label and the value to be tested) suitable to be passed to [`Assume`]().
/// This operator's purpose is to accept more terms than just functions as a contract: it is
/// the identity on functions, but it also accepts contracts as records, which are translated
/// to a function that performs a merge operation with its argument.
ToCtrFun(),
/// Typecast an enum to a larger enum type. /// Typecast an enum to a larger enum type.
/// ///
@ -632,9 +638,7 @@ pub enum BinaryOp {
/// ///
/// Apply a contract to a label and a value. The label and the value are stored on the stack, /// Apply a contract to a label and a value. The label and the value are stored on the stack,
/// unevaluated, while the contract is the strict argument to this operator. This operator /// unevaluated, while the contract is the strict argument to this operator. This operator
/// additionally marks the location of the tested value for better error reporting. It also /// additionally marks the location of the tested value for better error reporting.
/// accepts contracts as records, which are translated to a merge operation instead of function
/// application.
Assume(), Assume(),
/// Unwrap a tagged term. /// Unwrap a tagged term.
/// ///

View File

@ -814,7 +814,7 @@ pub fn apparent_type(t: &Term, envs: Option<&Envs>) -> ApparentType {
match t { match t {
Term::Promise(ty, _, _) Term::Promise(ty, _, _)
| Term::MetaValue(MetaValue { | Term::MetaValue(MetaValue {
types: Some(Contract {types: ty, ..}), types: Some(Contract { types: ty, .. }),
.. ..
}) => ApparentType::Annotated(ty.clone()), }) => ApparentType::Annotated(ty.clone()),
// For metavalues, if there's no type annotation, choose the first contract appearing. // For metavalues, if there's no type annotation, choose the first contract appearing.
@ -1528,6 +1528,8 @@ pub fn get_uop_type(state: &mut State, op: &UnaryOp) -> Result<TypeWrapper, Type
} }
// Bool -> Bool // Bool -> Bool
UnaryOp::BoolNot() => mk_tyw_arrow!(AbsType::Bool(), AbsType::Bool()), UnaryOp::BoolNot() => mk_tyw_arrow!(AbsType::Bool(), AbsType::Bool()),
// This should not happen, as ToCtrFun() is only produced during evaluation.
UnaryOp::ToCtrFun() => panic!("cannot typecheck ToCtrFun()"),
// forall a. Dyn -> a // forall a. Dyn -> a
UnaryOp::Blame() => { UnaryOp::Blame() => {
let res = TypeWrapper::Ptr(new_var(state.table)); let res = TypeWrapper::Ptr(new_var(state.table));

View File

@ -53,7 +53,7 @@
//! untyped parts. //! untyped parts.
use crate::identifier::Ident; use crate::identifier::Ident;
use crate::term::make as mk_term; use crate::term::make as mk_term;
use crate::term::{RichTerm, Term, UnaryOp, BinaryOp}; use crate::term::{BinaryOp, RichTerm, Term, UnaryOp};
use crate::{mk_app, mk_fun}; use crate::{mk_app, mk_fun};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
@ -180,7 +180,7 @@ impl Types {
) -> RichTerm { ) -> RichTerm {
use crate::stdlib::contracts; use crate::stdlib::contracts;
let ctr = match self.0 { match self.0 {
AbsType::Dyn() => contracts::dynamic(), AbsType::Dyn() => contracts::dynamic(),
AbsType::Num() => contracts::num(), AbsType::Num() => contracts::num(),
AbsType::Bool() => contracts::bool(), AbsType::Bool() => contracts::bool(),
@ -189,19 +189,17 @@ impl Types {
//always successful contract on each element. //always successful contract on each element.
AbsType::List(ref ty) => mk_app!(contracts::list(), ty.contract_open(h, pol, sy)), AbsType::List(ref ty) => mk_app!(contracts::list(), ty.contract_open(h, pol, sy)),
AbsType::Sym() => panic!("Are you trying to check a Sym at runtime?"), AbsType::Sym() => panic!("Are you trying to check a Sym at runtime?"),
AbsType::Arrow(ref s, ref t) => { AbsType::Arrow(ref s, ref t) => mk_app!(
let res = mk_app!(
contracts::func(), contracts::func(),
s.contract_open(h.clone(), !pol, sy), s.contract_open(h.clone(), !pol, sy),
t.contract_open(h, pol, sy) t.contract_open(h, pol, sy)
); ),
println!("Resulting contract: {:#?}", res);
res
}
AbsType::Flat(ref t) => { AbsType::Flat(ref t) => {
// The assume primive op being strict in the label, we need to map the contract in // As contracts may be specified either as function or records, we apply the ToCtrFun
// a function first. // operator, specifically to generate a uniform function of the label and the value to be
mk_fun!("l", mk_term::op2(BinaryOp::Assume(), t.clone(), mk_term::var("l"))) // tested.
let pos = t.pos;
mk_term::op1(UnaryOp::ToCtrFun(), t.clone()).with_pos(pos.into_inherited())
} }
AbsType::Var(ref i) => { AbsType::Var(ref i) => {
let (rt, _) = h let (rt, _) = h
@ -294,12 +292,7 @@ impl Types {
AbsType::DynRecord(ref ty) => { AbsType::DynRecord(ref ty) => {
mk_app!(contracts::dyn_record(), ty.contract_open(h, pol, sy)) mk_app!(contracts::dyn_record(), ty.contract_open(h, pol, sy))
} }
}; }
// The assume primive op being strict in the label, we need to map contracts as a lazy
// function.
let pos = ctr.pos;
mk_fun!("l", mk_term::op2(BinaryOp::Assume(), ctr, mk_term::var("l"))).with_pos(pos.into_inherited())
} }
/// Find a binding in a record row type. Return `None` if there is no such binding, if the type /// Find a binding in a record row type. Return `None` if there is no such binding, if the type