mirror of
https://github.com/tweag/nickel.git
synced 2024-10-06 08:07:37 +03:00
Make rustfmt work in match_sharedterm! (#1677)
* Make rustfmt work in match_sharedterm! macros can have syntax that rustfmt doens't know how to deal with, so it only triggers on macros that 1. are called with `()` or `[]` delimiters (not `{}`) 2. contain only valid rust syntax our match_sharedterm! failed on both those counts, so this commit fixes that. we move the `else` clause back inside the match statement as a wildcard arm, and use the `match` keyword so that rustfmt understands how we want the code formatted * Update core/src/term/mod.rs Co-authored-by: Yann Hamdaoui <yann.hamdaoui@tweag.io> --------- Co-authored-by: Yann Hamdaoui <yann.hamdaoui@tweag.io>
This commit is contained in:
parent
7e4ecd908c
commit
859293a3e5
@ -407,7 +407,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
// a definition, we complete the error with the additional information of where
|
||||
// it was accessed:
|
||||
let Closure { body, env } = self.cache.get(idx);
|
||||
let body = match_sharedterm! {body.term, with {
|
||||
let body = match_sharedterm!(match (body.term) {
|
||||
Term::RuntimeError(EvalError::MissingFieldDef {
|
||||
id,
|
||||
metadata,
|
||||
@ -422,10 +422,8 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
}),
|
||||
pos,
|
||||
),
|
||||
} else {
|
||||
body
|
||||
}
|
||||
};
|
||||
_ => body,
|
||||
});
|
||||
|
||||
Closure { body, env }
|
||||
}
|
||||
@ -910,24 +908,28 @@ pub fn env_add_record<C: Cache>(
|
||||
env: &mut Environment,
|
||||
closure: Closure,
|
||||
) -> Result<(), EnvBuildError> {
|
||||
match_sharedterm! {closure.body.term, with {
|
||||
match_sharedterm!(match (closure.body.term) {
|
||||
Term::Record(record) | Term::RecRecord(record, ..) => {
|
||||
let ext = record.fields.into_iter().filter_map(|(id, field)| {
|
||||
field.value.map(|value|
|
||||
field.value.map(|value| {
|
||||
(
|
||||
id.ident(),
|
||||
cache.add(
|
||||
Closure { body: value, env: closure.env.clone() },
|
||||
BindingType::Normal
|
||||
Closure {
|
||||
body: value,
|
||||
env: closure.env.clone(),
|
||||
},
|
||||
BindingType::Normal,
|
||||
),
|
||||
))
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
env.extend(ext);
|
||||
Ok(())
|
||||
},
|
||||
} else Err(EnvBuildError::NotARecord(closure.body))
|
||||
}
|
||||
_ => Err(EnvBuildError::NotARecord(closure.body)),
|
||||
})
|
||||
}
|
||||
|
||||
/// Bind a closure in an environment.
|
||||
|
@ -292,16 +292,14 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
Err(mk_type_error!("unary negation", "Bool"))
|
||||
}
|
||||
}
|
||||
UnaryOp::Blame() => match_sharedterm! { t, with {
|
||||
Term::Lbl(label) => Err(
|
||||
EvalError::BlameError {
|
||||
UnaryOp::Blame() => match_sharedterm!(match (t) {
|
||||
Term::Lbl(label) => Err(EvalError::BlameError {
|
||||
evaluated_arg: label.get_evaluated_arg(&self.cache),
|
||||
label,
|
||||
call_stack: std::mem::take(&mut self.call_stack),
|
||||
}),
|
||||
} else
|
||||
Err(mk_type_error!("blame", "Label"))
|
||||
},
|
||||
_ => Err(mk_type_error!("blame", "Label")),
|
||||
}),
|
||||
UnaryOp::Embed(_id) => {
|
||||
if let Term::Enum(_) = &*t {
|
||||
Ok(Closure::atomic_closure(RichTerm {
|
||||
@ -362,7 +360,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
Err(mk_type_error!("match", "Enum", 2))
|
||||
}
|
||||
}
|
||||
UnaryOp::ChangePolarity() => match_sharedterm! {t, with {
|
||||
UnaryOp::ChangePolarity() => match_sharedterm!(match (t) {
|
||||
Term::Lbl(l) => {
|
||||
let mut l = l;
|
||||
l.polarity = l.polarity.flip();
|
||||
@ -371,10 +369,8 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos_op_inh,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("chng_pol", "Label"))
|
||||
}
|
||||
},
|
||||
_ => Err(mk_type_error!("chng_pol", "Label")),
|
||||
}),
|
||||
UnaryOp::Pol() => {
|
||||
if let Term::Lbl(l) = &*t {
|
||||
Ok(Closure::atomic_closure(RichTerm::new(
|
||||
@ -385,7 +381,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
Err(mk_type_error!("polarity", "Label"))
|
||||
}
|
||||
}
|
||||
UnaryOp::GoDom() => match_sharedterm! {t, with {
|
||||
UnaryOp::GoDom() => match_sharedterm!(match (t) {
|
||||
Term::Lbl(l) => {
|
||||
let mut l = l;
|
||||
l.path.push(ty_path::Elem::Domain);
|
||||
@ -394,11 +390,9 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos_op_inh,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("go_dom", "Label"))
|
||||
}
|
||||
},
|
||||
UnaryOp::GoCodom() => match_sharedterm! {t, with {
|
||||
_ => Err(mk_type_error!("go_dom", "Label")),
|
||||
}),
|
||||
UnaryOp::GoCodom() => match_sharedterm!(match (t) {
|
||||
Term::Lbl(l) => {
|
||||
let mut l = l;
|
||||
l.path.push(ty_path::Elem::Codomain);
|
||||
@ -407,11 +401,9 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos_op_inh,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("go_codom", "Label"))
|
||||
}
|
||||
},
|
||||
UnaryOp::GoArray() => match_sharedterm! {t, with {
|
||||
_ => Err(mk_type_error!("go_codom", "Label")),
|
||||
}),
|
||||
UnaryOp::GoArray() => match_sharedterm!(match (t) {
|
||||
Term::Lbl(l) => {
|
||||
let mut l = l;
|
||||
l.path.push(ty_path::Elem::Array);
|
||||
@ -420,11 +412,9 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos_op_inh,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("go_array", "Label"))
|
||||
}
|
||||
},
|
||||
UnaryOp::GoDict() => match_sharedterm! {t, with {
|
||||
_ => Err(mk_type_error!("go_array", "Label")),
|
||||
}),
|
||||
UnaryOp::GoDict() => match_sharedterm!(match (t) {
|
||||
Term::Lbl(l) => {
|
||||
let mut l = l;
|
||||
l.path.push(ty_path::Elem::Dict);
|
||||
@ -433,10 +423,8 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos_op_inh,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("go_dict", "Label"))
|
||||
}
|
||||
},
|
||||
_ => Err(mk_type_error!("go_dict", "Label")),
|
||||
}),
|
||||
UnaryOp::StaticAccess(id) => {
|
||||
if let Term::Record(record) = &*t {
|
||||
// We have to apply potentially pending contracts. Right now, this
|
||||
@ -481,14 +469,15 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
))
|
||||
}
|
||||
}
|
||||
UnaryOp::FieldsOf() => match_sharedterm! {t, with {
|
||||
UnaryOp::FieldsOf() => match_sharedterm!(match (t) {
|
||||
Term::Record(record) => {
|
||||
let mut fields: Vec<String> = record
|
||||
.fields
|
||||
.into_iter()
|
||||
// Ignore optional fields without definitions.
|
||||
.filter_map(|(id, field)|
|
||||
(!field.is_empty_optional()).then(|| id.to_string()))
|
||||
.filter_map(|(id, field)| {
|
||||
(!field.is_empty_optional()).then(|| id.to_string())
|
||||
})
|
||||
.collect();
|
||||
fields.sort();
|
||||
let terms = fields.into_iter().map(mk_term::string).collect();
|
||||
@ -498,11 +487,9 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos_op_inh,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("fields", "Record"))
|
||||
}
|
||||
},
|
||||
UnaryOp::ValuesOf() => match_sharedterm! {t, with {
|
||||
_ => Err(mk_type_error!("fields", "Record")),
|
||||
}),
|
||||
UnaryOp::ValuesOf() => match_sharedterm!(match (t) {
|
||||
Term::Record(record) => {
|
||||
let mut values = record
|
||||
.into_iter_without_opts()
|
||||
@ -515,23 +502,18 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
Ok(Closure {
|
||||
// TODO: once sure that the Record is properly closurized, we can safely
|
||||
// assume that the extracted array here is, in turn, also closuried.
|
||||
body: RichTerm::new(
|
||||
Term::Array(terms, ArrayAttrs::default()),
|
||||
pos_op_inh
|
||||
),
|
||||
body: RichTerm::new(Term::Array(terms, ArrayAttrs::default()), pos_op_inh),
|
||||
env,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("values", "Record"))
|
||||
}
|
||||
},
|
||||
_ => Err(mk_type_error!("values", "Record")),
|
||||
}),
|
||||
UnaryOp::ArrayMap() => {
|
||||
let (f, ..) = self
|
||||
.stack
|
||||
.pop_arg(&self.cache)
|
||||
.ok_or_else(|| EvalError::NotEnoughArgs(2, String::from("map"), pos_op))?;
|
||||
match_sharedterm! {t, with {
|
||||
match_sharedterm!(match (t) {
|
||||
Term::Array(ts, attrs) => {
|
||||
let mut shared_env = Environment::new();
|
||||
let f_as_var = f.body.closurize(&mut self.cache, &mut env, f.env);
|
||||
@ -548,10 +530,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos.into_inherited(),
|
||||
);
|
||||
|
||||
RichTerm::new(
|
||||
Term::App(f_as_var.clone(), t_with_ctrs),
|
||||
pos_op_inh
|
||||
)
|
||||
RichTerm::new(Term::App(f_as_var.clone(), t_with_ctrs), pos_op_inh)
|
||||
.closurize(&mut self.cache, &mut shared_env, env.clone())
|
||||
})
|
||||
.collect();
|
||||
@ -559,15 +538,13 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
Ok(Closure {
|
||||
body: RichTerm::new(
|
||||
Term::Array(ts, attrs.contracts_cleared().closurized()),
|
||||
pos_op_inh
|
||||
pos_op_inh,
|
||||
),
|
||||
env: shared_env,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("map", "Array"))
|
||||
}
|
||||
}
|
||||
_ => Err(mk_type_error!("map", "Array")),
|
||||
})
|
||||
}
|
||||
UnaryOp::ArrayGen() => {
|
||||
let (f, _) = self
|
||||
@ -628,7 +605,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
EvalError::NotEnoughArgs(2, String::from("record_map"), pos_op)
|
||||
})?;
|
||||
|
||||
match_sharedterm! {t, with {
|
||||
match_sharedterm!(match (t) {
|
||||
Term::Record(record) => {
|
||||
// While it's certainly possible to allow mapping over
|
||||
// a record with a sealed tail, it's not entirely obvious
|
||||
@ -642,7 +619,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
evaluated_arg: label.get_evaluated_arg(&self.cache),
|
||||
label,
|
||||
call_stack: std::mem::take(&mut self.call_stack),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
let mut shared_env = Environment::new();
|
||||
@ -654,32 +631,31 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
.fields
|
||||
.into_iter()
|
||||
.filter(|(_, field)| !field.is_empty_optional())
|
||||
.map_values_closurize(&mut self.cache, &mut shared_env, &env,
|
||||
.map_values_closurize(
|
||||
&mut self.cache,
|
||||
&mut shared_env,
|
||||
&env,
|
||||
|id, t| {
|
||||
let pos = t.pos.into_inherited();
|
||||
|
||||
mk_app!(
|
||||
f_as_var.clone(),
|
||||
mk_term::string(id.label()), t
|
||||
)
|
||||
mk_app!(f_as_var.clone(), mk_term::string(id.label()), t)
|
||||
.with_pos(pos)
|
||||
}
|
||||
},
|
||||
)
|
||||
.map_err(|missing_field_err|
|
||||
missing_field_err.into_eval_err(pos, pos_op))?;
|
||||
.map_err(|missing_field_err| {
|
||||
missing_field_err.into_eval_err(pos, pos_op)
|
||||
})?;
|
||||
|
||||
Ok(Closure {
|
||||
body: RichTerm::new(
|
||||
Term::Record(RecordData { fields, ..record }),
|
||||
pos_op_inh
|
||||
pos_op_inh,
|
||||
),
|
||||
env: shared_env,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("record_map", "Record", 1))
|
||||
}
|
||||
}
|
||||
_ => Err(mk_type_error!("record_map", "Record", 1)),
|
||||
})
|
||||
}
|
||||
UnaryOp::Seq() => self
|
||||
.stack
|
||||
@ -881,21 +857,20 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
}
|
||||
}
|
||||
UnaryOp::ToStr() => {
|
||||
let result = match_sharedterm! {t, with {
|
||||
let result = match_sharedterm!(match (t) {
|
||||
Term::Num(n) => Ok(Term::Str(format!("{}", n.to_sci()).into())),
|
||||
Term::Str(s) => Ok(Term::Str(s)),
|
||||
Term::Bool(b) => Ok(Term::Str(b.to_string().into())),
|
||||
Term::Enum(id) => Ok(Term::Str(id.into())),
|
||||
Term::Null => Ok(Term::Str("null".into())),
|
||||
} else {
|
||||
Err(EvalError::Other(
|
||||
_ => Err(EvalError::Other(
|
||||
format!(
|
||||
"to_string: can't convert an argument of type {} to string",
|
||||
t.type_of().unwrap()
|
||||
),
|
||||
pos,
|
||||
))
|
||||
}}?;
|
||||
)),
|
||||
})?;
|
||||
|
||||
Ok(Closure::atomic_closure(RichTerm::new(result, pos_op_inh)))
|
||||
}
|
||||
@ -1036,33 +1011,36 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
.with_pos(pos)
|
||||
}
|
||||
|
||||
match_sharedterm! {t,
|
||||
with {
|
||||
match_sharedterm!(match (t) {
|
||||
Term::Record(record) if !record.fields.is_empty() => {
|
||||
let mut shared_env = Environment::new();
|
||||
|
||||
let fields = record.fields
|
||||
let fields = record
|
||||
.fields
|
||||
.into_iter()
|
||||
.filter(|(_, field)| {
|
||||
!(field.is_empty_optional()
|
||||
|| (ignore_not_exported && field.metadata.not_exported))
|
||||
})
|
||||
.map_values_closurize(&mut self.cache, &mut shared_env, &env,
|
||||
.map_values_closurize(
|
||||
&mut self.cache,
|
||||
&mut shared_env,
|
||||
&env,
|
||||
|_, value| {
|
||||
mk_term::op1(UnaryOp::Force { ignore_not_exported }, value)
|
||||
}
|
||||
mk_term::op1(
|
||||
UnaryOp::Force {
|
||||
ignore_not_exported,
|
||||
},
|
||||
value,
|
||||
)
|
||||
},
|
||||
)
|
||||
.map_err(|e| e.into_eval_err(pos, pos_op))?;
|
||||
|
||||
let terms = fields
|
||||
.clone()
|
||||
.into_values()
|
||||
.map(|field| {
|
||||
field
|
||||
.value
|
||||
.expect(
|
||||
let terms = fields.clone().into_values().map(|field| {
|
||||
field.value.expect(
|
||||
"map_values_closurize ensures that values without a \
|
||||
definition throw a MissingFieldDefError"
|
||||
definition throw a MissingFieldDefError",
|
||||
)
|
||||
});
|
||||
|
||||
@ -1075,21 +1053,27 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
body: seq_terms(terms, pos_op, cont),
|
||||
env: shared_env,
|
||||
})
|
||||
},
|
||||
}
|
||||
Term::Array(ts, attrs) if !ts.is_empty() => {
|
||||
let mut shared_env = Environment::new();
|
||||
let ts = ts
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
mk_term::op1(
|
||||
UnaryOp::Force { ignore_not_exported },
|
||||
UnaryOp::Force {
|
||||
ignore_not_exported,
|
||||
},
|
||||
RuntimeContract::apply_all(
|
||||
t,
|
||||
attrs.pending_contracts.iter().cloned(),
|
||||
pos.into_inherited(),
|
||||
),
|
||||
)
|
||||
.closurize(&mut self.cache, &mut shared_env, env.clone())
|
||||
.closurize(
|
||||
&mut self.cache,
|
||||
&mut shared_env,
|
||||
env.clone(),
|
||||
)
|
||||
})
|
||||
// It's important to collect here, otherwise the two usages below
|
||||
// will each do their own .closurize(...) calls and end up with
|
||||
@ -1105,32 +1089,29 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
env: shared_env,
|
||||
})
|
||||
}
|
||||
} else Ok(Closure {
|
||||
body: RichTerm { term : t, pos},
|
||||
_ => Ok(Closure {
|
||||
body: RichTerm { term: t, pos },
|
||||
env
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
UnaryOp::RecDefault() => {
|
||||
Ok(RecPriority::Bottom.propagate_in_term(&mut self.cache, t, env, pos))
|
||||
}
|
||||
UnaryOp::RecForce() => {
|
||||
Ok(RecPriority::Top.propagate_in_term(&mut self.cache, t, env, pos))
|
||||
}
|
||||
UnaryOp::RecordEmptyWithTail() => match_sharedterm! { t,
|
||||
with {
|
||||
UnaryOp::RecordEmptyWithTail() => match_sharedterm!(match (t) {
|
||||
Term::Record(r) => {
|
||||
let mut empty = RecordData::empty();
|
||||
empty.sealed_tail = r.sealed_tail;
|
||||
Ok(Closure {
|
||||
body: RichTerm::new(Term::Record(empty), pos_op.into_inherited()),
|
||||
env
|
||||
env,
|
||||
})
|
||||
},
|
||||
} else {
|
||||
Err(mk_type_error!("record_empty_with_tail", "Record"))
|
||||
}
|
||||
},
|
||||
_ => Err(mk_type_error!("record_empty_with_tail", "Record")),
|
||||
}),
|
||||
UnaryOp::Trace() => {
|
||||
if let Term::Str(s) = &*t {
|
||||
let _ = writeln!(self.trace, "std.trace: {s}");
|
||||
@ -1145,18 +1126,17 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
.ok_or_else(|| EvalError::NotEnoughArgs(2, String::from("trace"), pos_op))
|
||||
}
|
||||
UnaryOp::LabelPushDiag() => {
|
||||
match_sharedterm! {t, with {
|
||||
match_sharedterm!(match (t) {
|
||||
Term::Lbl(label) => {
|
||||
let mut label = label;
|
||||
label.push_diagnostic();
|
||||
Ok(Closure {
|
||||
body: RichTerm::new(Term::Lbl(label), pos),
|
||||
env
|
||||
env,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("label_push_diag", "Label"))
|
||||
}}
|
||||
_ => Err(mk_type_error!("label_push_diag", "Label")),
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "nix-experimental")]
|
||||
UnaryOp::EvalNix() => {
|
||||
@ -1574,8 +1554,8 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
Err(mk_type_error!("(>=)", "Number", 1, t1, pos1))
|
||||
}
|
||||
}
|
||||
BinaryOp::GoField() => match_sharedterm! {t1, with {
|
||||
Term::Str(field) => match_sharedterm! {t2, with {
|
||||
BinaryOp::GoField() => match_sharedterm!(match (t1) {
|
||||
Term::Str(field) => match_sharedterm!(match (t2) {
|
||||
Term::Lbl(l) => {
|
||||
let mut l = l;
|
||||
l.path.push(ty_path::Elem::Field(field.into_inner().into()));
|
||||
@ -1584,15 +1564,12 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos_op_inh,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("go_field", "Label", 2, t2, pos2))
|
||||
}
|
||||
},
|
||||
} else {
|
||||
Err(mk_type_error!("go_field", "String", 1, t1, pos1))
|
||||
}
|
||||
},
|
||||
BinaryOp::DynAccess() => match_sharedterm! {t1, with {
|
||||
_ => Err(mk_type_error!("go_field", "Label", 2, t2, pos2)),
|
||||
}),
|
||||
_ => Err(mk_type_error!("go_field", "String", 1, t1, pos1)),
|
||||
}),
|
||||
BinaryOp::DynAccess() => {
|
||||
match_sharedterm!(match (t1) {
|
||||
Term::Str(id) => {
|
||||
if let Term::Record(record) = &*t2 {
|
||||
// We have to apply potential pending contracts. Right now, this
|
||||
@ -1601,12 +1578,9 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
// contracts. There are several way to improve this, but this is left
|
||||
// as future work.
|
||||
let ident = LocIdent::from(&id);
|
||||
match record
|
||||
.get_value_with_ctrs(&ident)
|
||||
.map_err(|missing_field_err| {
|
||||
missing_field_err.into_eval_err(pos2, pos_op)
|
||||
})?
|
||||
{
|
||||
match record.get_value_with_ctrs(&ident).map_err(
|
||||
|missing_field_err| missing_field_err.into_eval_err(pos2, pos_op),
|
||||
)? {
|
||||
Some(value) => {
|
||||
self.call_stack.enter_field(ident, pos2, value.pos, pos_op);
|
||||
Ok(Closure {
|
||||
@ -1615,16 +1589,16 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
})
|
||||
}
|
||||
None => match record.sealed_tail.as_ref() {
|
||||
Some(t) if t.has_dyn_field(&id) =>
|
||||
Some(t) if t.has_dyn_field(&id) => {
|
||||
Err(EvalError::IllegalPolymorphicTailAccess {
|
||||
action: IllegalPolymorphicTailAction::FieldAccess {
|
||||
field: id.to_string()
|
||||
field: id.to_string(),
|
||||
},
|
||||
evaluated_arg: t.label.get_evaluated_arg(&self.cache),
|
||||
label: t.label.clone(),
|
||||
call_stack: std::mem::take(&mut self.call_stack)
|
||||
call_stack: std::mem::take(&mut self.call_stack),
|
||||
})
|
||||
}
|
||||
),
|
||||
_ => Err(EvalError::FieldMissing(
|
||||
id.into_inner(),
|
||||
String::from("(.$)"),
|
||||
@ -1634,8 +1608,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
},
|
||||
pos_op,
|
||||
)),
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Not using mk_type_error! because of a non-uniform message
|
||||
@ -1650,13 +1623,12 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This error should be impossible to trigger. The parser
|
||||
// prevents a dynamic field access where the field name is not syntactically
|
||||
// a string.
|
||||
Err(mk_type_error!(".$", "String", 1, t1, pos1))
|
||||
_ => Err(mk_type_error!(".$", "String", 1, t1, pos1)),
|
||||
})
|
||||
}
|
||||
},
|
||||
BinaryOp::DynExtend {
|
||||
metadata,
|
||||
pending_contracts,
|
||||
@ -1664,78 +1636,90 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
op_kind,
|
||||
} => {
|
||||
if let Term::Str(id) = &*t1 {
|
||||
match_sharedterm! {t2, with {
|
||||
match_sharedterm!(match (t2) {
|
||||
Term::Record(record) => {
|
||||
let mut fields = record.fields;
|
||||
|
||||
// If a defined value is expected for this field, it must be
|
||||
// provided as an additional argument, so we pop it from the stack
|
||||
let value = if let RecordExtKind::WithValue = ext_kind {
|
||||
let (value_closure, _) = self
|
||||
.stack
|
||||
.pop_arg(&self.cache)
|
||||
.ok_or_else(|| EvalError::NotEnoughArgs(
|
||||
3,
|
||||
String::from("insert"),
|
||||
pos_op
|
||||
))?;
|
||||
let (value_closure, _) =
|
||||
self.stack.pop_arg(&self.cache).ok_or_else(|| {
|
||||
EvalError::NotEnoughArgs(3, String::from("insert"), pos_op)
|
||||
})?;
|
||||
|
||||
let as_var = value_closure.body
|
||||
.closurize(&mut self.cache, &mut env2, value_closure.env);
|
||||
let as_var = value_closure.body.closurize(
|
||||
&mut self.cache,
|
||||
&mut env2,
|
||||
value_closure.env,
|
||||
);
|
||||
Some(as_var)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match fields.insert(
|
||||
LocIdent::from(id),
|
||||
Field { value, metadata, pending_contracts }
|
||||
Field {
|
||||
value,
|
||||
metadata,
|
||||
pending_contracts,
|
||||
},
|
||||
) {
|
||||
Some(t) if matches!(op_kind, RecordOpKind::ConsiderAllFields)
|
||||
Some(t)
|
||||
if matches!(op_kind, RecordOpKind::ConsiderAllFields)
|
||||
|| !t.is_empty_optional() =>
|
||||
Err(EvalError::Other(format!(
|
||||
{
|
||||
Err(EvalError::Other(
|
||||
format!(
|
||||
"record_insert: \
|
||||
tried to extend a record with the field {id}, \
|
||||
but it already exists"
|
||||
), pos_op)),
|
||||
),
|
||||
pos_op,
|
||||
))
|
||||
}
|
||||
_ => Ok(Closure {
|
||||
body: Term::Record(RecordData { fields, ..record }).into(),
|
||||
env: env2,
|
||||
}),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("record_insert", "Record", 2, t2, pos2))
|
||||
}
|
||||
}
|
||||
_ => Err(mk_type_error!("record_insert", "Record", 2, t2, pos2)),
|
||||
})
|
||||
} else {
|
||||
Err(mk_type_error!("record_insert", "String", 1, t1, pos1))
|
||||
}
|
||||
}
|
||||
BinaryOp::DynRemove(op_kind) => match_sharedterm! {t1, with {
|
||||
Term::Str(id) => match_sharedterm! {t2, with {
|
||||
BinaryOp::DynRemove(op_kind) => match_sharedterm!(match (t1) {
|
||||
Term::Str(id) => match_sharedterm!(match (t2) {
|
||||
Term::Record(record) => {
|
||||
let mut fields = record.fields;
|
||||
let fetched = fields.remove(&LocIdent::from(&id));
|
||||
if fetched.is_none()
|
||||
|| matches!((op_kind, fetched), (RecordOpKind::IgnoreEmptyOpt, Some(Field {
|
||||
|| matches!(
|
||||
(op_kind, fetched),
|
||||
(
|
||||
RecordOpKind::IgnoreEmptyOpt,
|
||||
Some(Field {
|
||||
value: None,
|
||||
metadata: FieldMetadata { opt: true, ..},
|
||||
metadata: FieldMetadata { opt: true, .. },
|
||||
..
|
||||
}))) {
|
||||
})
|
||||
)
|
||||
)
|
||||
{
|
||||
match record.sealed_tail.as_ref() {
|
||||
Some(t) if t.has_dyn_field(&id) =>
|
||||
Some(t) if t.has_dyn_field(&id) => {
|
||||
Err(EvalError::IllegalPolymorphicTailAccess {
|
||||
action: IllegalPolymorphicTailAction::RecordRemove {
|
||||
field: id.to_string()
|
||||
field: id.to_string(),
|
||||
},
|
||||
evaluated_arg: t.label
|
||||
.get_evaluated_arg(&self.cache),
|
||||
evaluated_arg: t.label.get_evaluated_arg(&self.cache),
|
||||
label: t.label.clone(),
|
||||
call_stack: std::mem::take(&mut self.call_stack)
|
||||
call_stack: std::mem::take(&mut self.call_stack),
|
||||
})
|
||||
}
|
||||
),
|
||||
_ => Err(EvalError::FieldMissing(
|
||||
id.into_inner(),
|
||||
String::from("record_remove"),
|
||||
@ -1746,26 +1730,21 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
pos_op,
|
||||
)),
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Ok(Closure {
|
||||
body: RichTerm::new(
|
||||
Term::Record(RecordData { fields, ..record }),
|
||||
pos_op_inh
|
||||
pos_op_inh,
|
||||
),
|
||||
env: env2,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("record_remove", "Record", 2, t2, pos2))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("record_remove", "String", 1, t1, pos1))
|
||||
}
|
||||
},
|
||||
BinaryOp::HasField(op_kind) => match_sharedterm! {t1, with {
|
||||
_ => Err(mk_type_error!("record_remove", "Record", 2, t2, pos2)),
|
||||
}),
|
||||
_ => Err(mk_type_error!("record_remove", "String", 1, t1, pos1)),
|
||||
}),
|
||||
BinaryOp::HasField(op_kind) => match_sharedterm!(match (t1) {
|
||||
Term::Str(id) => {
|
||||
if let Term::Record(record) = &*t2 {
|
||||
Ok(Closure::atomic_closure(RichTerm::new(
|
||||
@ -1779,23 +1758,23 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
Err(mk_type_error!("has_field", "Record", 2, t2, pos2))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("has_field", "String", 1, t1, pos1))
|
||||
}
|
||||
},
|
||||
BinaryOp::ArrayConcat() => match_sharedterm! {t1,
|
||||
with {
|
||||
Term::Array(ts1, attrs1) => match_sharedterm! {t2,
|
||||
with {
|
||||
_ => Err(mk_type_error!("has_field", "String", 1, t1, pos1)),
|
||||
}),
|
||||
BinaryOp::ArrayConcat() => match_sharedterm!(match (t1) {
|
||||
Term::Array(ts1, attrs1) => match_sharedterm!(match (t2) {
|
||||
Term::Array(ts2, attrs2) => {
|
||||
// NOTE: the [eval_closure] function in [eval] should've made sure
|
||||
// that the array is closurized. We leave a debug_assert! here just
|
||||
// in case something goes wrong in the future. If the assert failed,
|
||||
// you may need to map closurize over `ts1` and `ts2`.
|
||||
debug_assert!(attrs1.closurized,
|
||||
"the left-hand side of ArrayConcat (@) is not closurized.");
|
||||
debug_assert!(attrs2.closurized,
|
||||
"the right-hand side of ArrayConcat (@) is not closurized.");
|
||||
debug_assert!(
|
||||
attrs1.closurized,
|
||||
"the left-hand side of ArrayConcat (@) is not closurized."
|
||||
);
|
||||
debug_assert!(
|
||||
attrs2.closurized,
|
||||
"the right-hand side of ArrayConcat (@) is not closurized."
|
||||
);
|
||||
|
||||
// NOTE: To avoid the extra Vec allocation, we could use
|
||||
// Rc<[T]>::new_uninit_slice() and fill up the slice manually, but
|
||||
@ -1808,9 +1787,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
// - Rc<[T]>::from_iter docs:
|
||||
// https://doc.rust-lang.org/std/rc/struct.Rc.html#impl-FromIterator%3CT%3E
|
||||
// - chain issue: https://github.com/rust-lang/rust/issues/63340
|
||||
let mut ts: Vec<RichTerm> = Vec::with_capacity(
|
||||
ts1.len() + ts2.len()
|
||||
);
|
||||
let mut ts: Vec<RichTerm> = Vec::with_capacity(ts1.len() + ts2.len());
|
||||
|
||||
let mut env = env1.clone();
|
||||
// TODO: Is there a cheaper way to "merge" two environements?
|
||||
@ -1831,13 +1808,13 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
// We use a vector of `Option` so that we can set the elements to
|
||||
// remove to `None` and make a single pass at the end
|
||||
// to retain the remaining ones.
|
||||
let mut ctrs_right_sieve : Vec<_> = attrs2.pending_contracts.into_iter().map(Some).collect();
|
||||
let mut ctrs_right_sieve: Vec<_> =
|
||||
attrs2.pending_contracts.into_iter().map(Some).collect();
|
||||
let mut ctrs_common = Vec::new();
|
||||
|
||||
// We basically compute the intersection (`ctr_common`),
|
||||
// `ctrs_left - ctr_common`, and `ctrs_right - ctr_common`.
|
||||
let ctrs_left_dedup : Vec<_> =
|
||||
ctrs_left
|
||||
let ctrs_left: Vec<_> = ctrs_left
|
||||
.into_iter()
|
||||
.filter(|ctr| {
|
||||
// We don't deduplicate polymorphic contracts, because
|
||||
@ -1851,15 +1828,10 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
initial_env: &self.initial_env,
|
||||
};
|
||||
|
||||
// We check if there is a remaining contract in
|
||||
// `ctrs_right_sieve` which matches `ctr`: in this case,
|
||||
// `twin_index` will hold its index.
|
||||
let twin_index = ctrs_right_sieve
|
||||
.iter()
|
||||
.filter_map(|ctr| ctr.as_ref())
|
||||
.position(|other_ctr| {
|
||||
other_ctr
|
||||
.as_ref()
|
||||
.map_or(false, |other_ctr| {
|
||||
let envs_right = EvalEnvsRef {
|
||||
eval_env: &env2,
|
||||
initial_env: &self.initial_env,
|
||||
@ -1872,36 +1844,31 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
&other_ctr.contract,
|
||||
envs_right,
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(index) = twin_index {
|
||||
// unwrap(): we know that the contract at this index is
|
||||
// `Some`, because all elements are initially some when
|
||||
// creating `ctrs_right_sieve` and then we don't
|
||||
// consider `None` values when computing a new `index`
|
||||
// in the `position` above.
|
||||
let common = ctrs_right_sieve[index].take().unwrap();
|
||||
ctrs_common.push(common);
|
||||
ctrs_right_sieve[index] = None;
|
||||
false
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ctrs_right_dedup = ctrs_right_sieve.into_iter().flatten();
|
||||
let ctrs_right = ctrs_right_sieve.into_iter().flatten();
|
||||
|
||||
ts.extend(ts1.into_iter().map(|t|
|
||||
RuntimeContract::apply_all(t, ctrs_left_dedup.iter().cloned(), pos1)
|
||||
ts.extend(ts1.into_iter().map(|t| {
|
||||
RuntimeContract::apply_all(t, ctrs_left.iter().cloned(), pos1)
|
||||
.closurize(&mut self.cache, &mut env, env1.clone())
|
||||
));
|
||||
}));
|
||||
|
||||
ts.extend(ts2.into_iter().map(|t|
|
||||
RuntimeContract::apply_all(t, ctrs_right_dedup.clone(), pos2)
|
||||
.closurize(&mut self.cache, &mut env, env2.clone())
|
||||
));
|
||||
ts.extend(ts2.into_iter().map(|t| {
|
||||
RuntimeContract::apply_all(t, ctrs_right.clone(), pos2).closurize(
|
||||
&mut self.cache,
|
||||
&mut env,
|
||||
env2.clone(),
|
||||
)
|
||||
}));
|
||||
|
||||
let attrs = ArrayAttrs {
|
||||
closurized: true,
|
||||
@ -1911,20 +1878,15 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
Ok(Closure {
|
||||
body: RichTerm::new(
|
||||
Term::Array(Array::new(Rc::from(ts)), attrs),
|
||||
pos_op_inh
|
||||
pos_op_inh,
|
||||
),
|
||||
env,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(mk_type_error!("(@)", "Array", 2, t2, pos2))
|
||||
|
||||
}
|
||||
},
|
||||
} else {
|
||||
Err(mk_type_error!("(@)", "Array", 1, t1, pos1))
|
||||
}
|
||||
},
|
||||
_ => Err(mk_type_error!("(@)", "Array", 2, t2, pos2)),
|
||||
}),
|
||||
_ => Err(mk_type_error!("(@)", "Array", 1, t1, pos1)),
|
||||
}),
|
||||
BinaryOp::ArrayElemAt() => match (&*t1, &*t2) {
|
||||
(Term::Array(ts, attrs), Term::Num(n)) => {
|
||||
let Ok(n_as_usize) = usize::try_from(n) else {
|
||||
@ -2148,13 +2110,12 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
} = ctr;
|
||||
|
||||
// FIXME: use match?
|
||||
let lbl = match_sharedterm! {t1, with {
|
||||
Term::Lbl(lbl) => lbl
|
||||
} else return Err(mk_type_error!("array_lazy_app_ctr", "Label", 1, t1, pos1))
|
||||
};
|
||||
let lbl = match_sharedterm!(match (t1) {
|
||||
Term::Lbl(lbl) => lbl,
|
||||
_ => return Err(mk_type_error!("array_lazy_app_ctr", "Label", 1, t1, pos1)),
|
||||
});
|
||||
|
||||
match_sharedterm! {t2,
|
||||
with {
|
||||
match_sharedterm!(match (t2) {
|
||||
Term::Array(ts, attrs) => {
|
||||
let mut attrs = attrs;
|
||||
let mut final_env = env2;
|
||||
@ -2166,24 +2127,18 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
&mut attrs.pending_contracts,
|
||||
&final_env,
|
||||
RuntimeContract::new(contract, lbl),
|
||||
&final_env
|
||||
&final_env,
|
||||
);
|
||||
|
||||
let array_with_ctr = Closure {
|
||||
body: RichTerm::new(
|
||||
Term::Array(
|
||||
ts,
|
||||
attrs,
|
||||
),
|
||||
pos2,
|
||||
),
|
||||
body: RichTerm::new(Term::Array(ts, attrs), pos2),
|
||||
env: final_env,
|
||||
};
|
||||
|
||||
Ok(array_with_ctr)
|
||||
}
|
||||
} else Err(mk_type_error!("array_lazy_app_ctr", "Array", 2, t2, pos2))
|
||||
}
|
||||
_ => Err(mk_type_error!("array_lazy_app_ctr", "Array", 2, t2, pos2)),
|
||||
})
|
||||
}
|
||||
BinaryOp::RecordLazyAppCtr() => {
|
||||
// The contract is expected to be of type `String -> Contract`: it takes the name
|
||||
@ -2198,13 +2153,12 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
EvalError::NotEnoughArgs(3, String::from("record_lazy_app_ctr"), pos_op)
|
||||
})?;
|
||||
|
||||
let label = match_sharedterm! {t1, with {
|
||||
Term::Lbl(label) => label
|
||||
} else return Err(mk_type_error!("record_lazy_app_ctr", "Label", 1, t1, pos1))
|
||||
};
|
||||
let label = match_sharedterm!(match (t1) {
|
||||
Term::Lbl(label) => label,
|
||||
_ => return Err(mk_type_error!("record_lazy_app_ctr", "Label", 1, t1, pos1)),
|
||||
});
|
||||
|
||||
match_sharedterm! {t2,
|
||||
with {
|
||||
match_sharedterm!(match (t2) {
|
||||
Term::Record(record_data) => {
|
||||
// due to a limitation of `match_sharedterm`: see the macro's
|
||||
// documentation
|
||||
@ -2215,9 +2169,13 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
let pos = contract_term.pos;
|
||||
mk_app!(
|
||||
contract_term.clone(),
|
||||
RichTerm::new(Term::Str(id.into()), id.pos))
|
||||
RichTerm::new(Term::Str(id.into()), id.pos)
|
||||
)
|
||||
.with_pos(pos)
|
||||
.closurize(&mut self.cache, &mut env2, contract_env.clone(),
|
||||
.closurize(
|
||||
&mut self.cache,
|
||||
&mut env2,
|
||||
contract_env.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
@ -2245,20 +2203,16 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
// well: hence, we need to recompute the fixpoint, which is done by
|
||||
// `fixpoint::revert`.
|
||||
let mut env = Environment::new();
|
||||
let reverted = super::fixpoint::revert(
|
||||
&mut self.cache,
|
||||
record_data,
|
||||
&mut env,
|
||||
&env2
|
||||
);
|
||||
let reverted =
|
||||
super::fixpoint::revert(&mut self.cache, record_data, &mut env, &env2);
|
||||
|
||||
Ok(Closure {
|
||||
body: RichTerm::new(reverted, pos2),
|
||||
env,
|
||||
})
|
||||
}
|
||||
} else Err(mk_type_error!("record_lazy_app_ctr", "Record", 2, t2, pos2))
|
||||
}
|
||||
_ => Err(mk_type_error!("record_lazy_app_ctr", "Record", 2, t2, pos2)),
|
||||
})
|
||||
}
|
||||
BinaryOp::LabelWithMessage() => {
|
||||
let t1 = t1.into_owned();
|
||||
@ -2564,7 +2518,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
) = args_iter.next().unwrap();
|
||||
debug_assert!(args_iter.next().is_none());
|
||||
|
||||
match_sharedterm! {t1, with {
|
||||
match_sharedterm!(match (t1) {
|
||||
Term::Lbl(lbl) => {
|
||||
merge::merge(
|
||||
&mut self.cache,
|
||||
@ -2581,20 +2535,19 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
|
||||
env3,
|
||||
pos_op,
|
||||
MergeMode::Contract(lbl),
|
||||
&mut self.call_stack
|
||||
&mut self.call_stack,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Err(EvalError::InternalError(
|
||||
_ => Err(EvalError::InternalError(
|
||||
format!(
|
||||
"The MergeContract() operator was expecting \
|
||||
a first argument of type Label, got {}",
|
||||
t1.type_of().unwrap_or_else(|| String::from("<unevaluated>"))
|
||||
t1.type_of()
|
||||
.unwrap_or_else(|| String::from("<unevaluated>"))
|
||||
),
|
||||
pos_op
|
||||
))
|
||||
}
|
||||
}
|
||||
)),
|
||||
})
|
||||
}
|
||||
NAryOp::RecordSealTail() => {
|
||||
let mut args = args.into_iter();
|
||||
|
@ -474,7 +474,7 @@ impl<EC: EvalCache> Program<EC> {
|
||||
_ => result,
|
||||
}?;
|
||||
|
||||
match_sharedterm! {rt.term, with {
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::Record(data) => {
|
||||
let fields = data
|
||||
.fields
|
||||
@ -502,8 +502,8 @@ impl<EC: EvalCache> Program<EC> {
|
||||
rt.pos,
|
||||
))
|
||||
}
|
||||
} else Ok(rt)
|
||||
}
|
||||
_ => Ok(rt),
|
||||
})
|
||||
}
|
||||
|
||||
do_eval(&mut self.vm, t, Environment::new())
|
||||
|
@ -1780,49 +1780,34 @@ impl Traverse<RichTerm> for RichTerm {
|
||||
};
|
||||
let pos = rt.pos;
|
||||
|
||||
let result = match_sharedterm! {rt.term, with {
|
||||
let result = match_sharedterm!(match (rt.term) {
|
||||
Term::Fun(id, t) => {
|
||||
let t = t.traverse(f, order)?;
|
||||
RichTerm::new(
|
||||
Term::Fun(id, t),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::Fun(id, t), pos)
|
||||
}
|
||||
Term::FunPattern(id, d, t) => {
|
||||
let t = t.traverse(f, order)?;
|
||||
RichTerm::new(
|
||||
Term::FunPattern(id, d, t),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::FunPattern(id, d, t), pos)
|
||||
}
|
||||
Term::Let(id, t1, t2, attrs) => {
|
||||
let t1 = t1.traverse(f, order)?;
|
||||
let t2 = t2.traverse(f, order)?;
|
||||
RichTerm::new(
|
||||
Term::Let(id, t1, t2, attrs),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::Let(id, t1, t2, attrs), pos)
|
||||
}
|
||||
Term::LetPattern(id, pat, t1, t2) => {
|
||||
let t1 = t1.traverse(f, order)?;
|
||||
let t2 = t2.traverse(f, order)?;
|
||||
RichTerm::new(
|
||||
Term::LetPattern(id, pat, t1, t2),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::LetPattern(id, pat, t1, t2), pos)
|
||||
}
|
||||
Term::App(t1, t2) => {
|
||||
let t1 = t1.traverse(f, order)?;
|
||||
let t2 = t2.traverse(f, order)?;
|
||||
RichTerm::new(
|
||||
Term::App(t1, t2),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::App(t1, t2), pos)
|
||||
}
|
||||
Term::Match { cases, default } => {
|
||||
// The annotation on `map_res` use Result's corresponding trait to convert from
|
||||
// Iterator<Result> to a Result<Iterator>
|
||||
let cases_result : Result<IndexMap<LocIdent, RichTerm>, E> = cases
|
||||
let cases_result: Result<IndexMap<LocIdent, RichTerm>, E> = cases
|
||||
.into_iter()
|
||||
// For the conversion to work, note that we need a Result<(Ident,RichTerm), E>
|
||||
.map(|(id, t)| t.traverse(f, order).map(|t_ok| (id, t_ok)))
|
||||
@ -1831,45 +1816,36 @@ impl Traverse<RichTerm> for RichTerm {
|
||||
let default = default.map(|t| t.traverse(f, order)).transpose()?;
|
||||
|
||||
RichTerm::new(
|
||||
Term::Match {cases: cases_result?, default },
|
||||
Term::Match {
|
||||
cases: cases_result?,
|
||||
default,
|
||||
},
|
||||
pos,
|
||||
)
|
||||
},
|
||||
}
|
||||
Term::Op1(op, t) => {
|
||||
let t = t.traverse(f, order)?;
|
||||
RichTerm::new(
|
||||
Term::Op1(op, t),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::Op1(op, t), pos)
|
||||
}
|
||||
Term::Op2(op, t1, t2) => {
|
||||
let t1 = t1.traverse(f, order)?;
|
||||
let t2 = t2.traverse(f, order)?;
|
||||
RichTerm::new(Term::Op2(op, t1, t2),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::Op2(op, t1, t2), pos)
|
||||
}
|
||||
Term::OpN(op, ts) => {
|
||||
let ts_res: Result<Vec<RichTerm>, E> = ts
|
||||
.into_iter()
|
||||
.map(|t| t.traverse(f, order))
|
||||
.collect();
|
||||
RichTerm::new(
|
||||
Term::OpN(op, ts_res?),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
let ts_res: Result<Vec<RichTerm>, E> =
|
||||
ts.into_iter().map(|t| t.traverse(f, order)).collect();
|
||||
RichTerm::new(Term::OpN(op, ts_res?), pos)
|
||||
}
|
||||
Term::Sealed(i, t1, lbl) => {
|
||||
let t1 = t1.traverse(f, order)?;
|
||||
RichTerm::new(
|
||||
Term::Sealed(i, t1, lbl),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::Sealed(i, t1, lbl), pos)
|
||||
}
|
||||
Term::Record(record) => {
|
||||
// The annotation on `fields_res` uses Result's corresponding trait to convert from
|
||||
// Iterator<Result> to a Result<Iterator>
|
||||
let fields_res: Result<IndexMap<LocIdent, Field>, E> = record.fields
|
||||
let fields_res: Result<IndexMap<LocIdent, Field>, E> = record
|
||||
.fields
|
||||
.into_iter()
|
||||
// For the conversion to work, note that we need a Result<(Ident,RichTerm), E>
|
||||
.map(|(id, field)| {
|
||||
@ -1878,14 +1854,19 @@ impl Traverse<RichTerm> for RichTerm {
|
||||
})
|
||||
.collect();
|
||||
RichTerm::new(
|
||||
Term::Record(RecordData::new(fields_res?, record.attrs, record.sealed_tail)),
|
||||
pos
|
||||
Term::Record(RecordData::new(
|
||||
fields_res?,
|
||||
record.attrs,
|
||||
record.sealed_tail,
|
||||
)),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
}
|
||||
Term::RecRecord(record, dyn_fields, deps) => {
|
||||
// The annotation on `map_res` uses Result's corresponding trait to convert from
|
||||
// Iterator<Result> to a Result<Iterator>
|
||||
let static_fields_res: Result<IndexMap<LocIdent, Field>, E> = record.fields
|
||||
let static_fields_res: Result<IndexMap<LocIdent, Field>, E> = record
|
||||
.fields
|
||||
.into_iter()
|
||||
// For the conversion to work, note that we need a Result<(Ident,Field), E>
|
||||
.map(|(id, field)| {
|
||||
@ -1899,33 +1880,27 @@ impl Traverse<RichTerm> for RichTerm {
|
||||
let id_t = id_t.traverse(f, order)?;
|
||||
let field = field.traverse(f, order)?;
|
||||
|
||||
Ok((id_t, field,))
|
||||
Ok((id_t, field))
|
||||
})
|
||||
.collect();
|
||||
RichTerm::new(
|
||||
Term::RecRecord(
|
||||
RecordData::new(
|
||||
static_fields_res?,
|
||||
record.attrs,
|
||||
record.sealed_tail
|
||||
),
|
||||
RecordData::new(static_fields_res?, record.attrs, record.sealed_tail),
|
||||
dyn_fields_res?,
|
||||
deps
|
||||
deps,
|
||||
),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
}
|
||||
Term::Array(ts, attrs) => {
|
||||
let ts_res = Array::new(ts
|
||||
.into_iter()
|
||||
let ts_res = Array::new(
|
||||
ts.into_iter()
|
||||
.map(|t| t.traverse(f, order))
|
||||
.collect::<Result<Rc<[_]>, _>>()?);
|
||||
.collect::<Result<Rc<[_]>, _>>()?,
|
||||
);
|
||||
|
||||
RichTerm::new(
|
||||
Term::Array(ts_res, attrs),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::Array(ts_res, attrs), pos)
|
||||
}
|
||||
Term::StrChunks(chunks) => {
|
||||
let chunks_res: Result<Vec<StrChunk<RichTerm>>, E> = chunks
|
||||
.into_iter()
|
||||
@ -1937,23 +1912,18 @@ impl Traverse<RichTerm> for RichTerm {
|
||||
})
|
||||
.collect();
|
||||
|
||||
RichTerm::new(
|
||||
Term::StrChunks(chunks_res?),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::StrChunks(chunks_res?), pos)
|
||||
}
|
||||
Term::Annotated(annot, term) => {
|
||||
let annot = annot.traverse(f, order)?;
|
||||
let term = term.traverse(f, order)?;
|
||||
RichTerm::new(
|
||||
Term::Annotated(annot, term),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
RichTerm::new(Term::Annotated(annot, term), pos)
|
||||
}
|
||||
Term::Type(ty) => {
|
||||
RichTerm::new(Term::Type(ty.traverse(f, order)?), pos)
|
||||
}
|
||||
} else rt};
|
||||
_ => rt,
|
||||
});
|
||||
|
||||
match order {
|
||||
TraverseOrder::TopDown => Ok(result),
|
||||
@ -2043,10 +2013,12 @@ impl Traverse<Type> for RichTerm {
|
||||
{
|
||||
self.traverse(
|
||||
&mut |rt: RichTerm| {
|
||||
match_sharedterm! {rt.term, with {
|
||||
Term::Type(ty) =>
|
||||
ty.traverse(f, order).map(|ty| RichTerm::new(Term::Type(ty), rt.pos))
|
||||
} else Ok(rt)}
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::Type(ty) => ty
|
||||
.traverse(f, order)
|
||||
.map(|ty| RichTerm::new(Term::Type(ty), rt.pos)),
|
||||
_ => Ok(rt),
|
||||
})
|
||||
},
|
||||
order,
|
||||
)
|
||||
@ -2100,9 +2072,9 @@ impl fmt::Display for Term {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows to match on SharedTerm without taking ownership of the matched part until the match. In
|
||||
/// the `else` clause, we haven't taken ownership yet, so we can still use the richterm at that
|
||||
/// point.
|
||||
/// Allows to match on SharedTerm without taking ownership of the matched part
|
||||
/// until the match. In the wildcard pattern, we don't take ownership, so we can
|
||||
/// still use the richterm at that point.
|
||||
///
|
||||
/// It is used somehow as a match statement, going from
|
||||
/// ```
|
||||
@ -2121,20 +2093,25 @@ impl fmt::Display for Term {
|
||||
/// # use nickel_lang_core::match_sharedterm;
|
||||
/// let rt = RichTerm::from(Term::Bool(true));
|
||||
///
|
||||
/// match_sharedterm!{rt.term, with {
|
||||
/// match_sharedterm!(match (rt.term) {
|
||||
/// Term::Bool(x) => usize::from(x),
|
||||
/// Term::Str(s) => s.len(),
|
||||
/// } else 42
|
||||
/// };
|
||||
/// _ => 42,
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// Unlike a regular match statement, the expression being matched on must be
|
||||
/// surrounded in parentheses
|
||||
///
|
||||
/// Known limitation: cannot use a `mut` inside the patterns.
|
||||
#[macro_export]
|
||||
macro_rules! match_sharedterm {
|
||||
(
|
||||
$st: expr, with {
|
||||
$($($pat: pat_param)|+ $(if $if_expr: expr)? => $expr: expr),+ $(,)?
|
||||
} else $else_clause: expr
|
||||
match ($st: expr) {
|
||||
$(%PROCESSED% $($pat: pat_param)|+ $(if $if_expr: expr)? => $expr: expr,)+
|
||||
_ => $else_expr: expr $(,)?
|
||||
}
|
||||
) => {
|
||||
match $st.as_ref() {
|
||||
$(
|
||||
@ -2145,9 +2122,56 @@ macro_rules! match_sharedterm {
|
||||
_ => unsafe {::core::hint::unreachable_unchecked()}
|
||||
},
|
||||
)+
|
||||
_ => $else_clause
|
||||
_ => $else_expr
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// recurse through the match arms prepending %PROCESSED% for two reasons:
|
||||
// 1. to standardize match arms with trailing commas on <pattern> => { <body> }
|
||||
// 2. so there's no ambiguity between a normal match arm and the final _ => <body>
|
||||
(
|
||||
match ($st: expr) {
|
||||
$(%PROCESSED% $($pat1: pat_param)|+ $(if $if_expr1: expr)? => $expr1: expr,)*
|
||||
$($pat2: pat_param)|+ $(if $if_expr2: expr)? => $expr2: expr,
|
||||
$($rest:tt)*
|
||||
}
|
||||
) => {
|
||||
match_sharedterm!(match ($st) {
|
||||
$(%PROCESSED% $($pat1)|+ $(if $if_expr1)? => $expr1,)*
|
||||
%PROCESSED% $($pat2)|+ $(if $if_expr2)? => $expr2,
|
||||
$($rest)*
|
||||
})
|
||||
};
|
||||
(
|
||||
match ($st: expr) {
|
||||
$(%PROCESSED% $($pat1: pat_param)|+ $(if $if_expr1: expr)? => $expr1: expr,)*
|
||||
$($pat2: pat_param)|+ $(if $if_expr2: expr)? => $expr2: block
|
||||
$($rest:tt)*
|
||||
}
|
||||
) => {
|
||||
match_sharedterm!(match ($st) {
|
||||
$(%PROCESSED% $($pat1)|+ $(if $if_expr1)? => $expr1,)*
|
||||
%PROCESSED% $($pat2)|+ $(if $if_expr2)? => $expr2,
|
||||
$($rest)*
|
||||
})
|
||||
};
|
||||
|
||||
// throw nice error messages for common mistakes
|
||||
(
|
||||
match ($st: expr) {
|
||||
$(%PROCESSED% $($pat: pat_param)|+ $(if $if_expr: expr)? => $expr: expr,)+
|
||||
}
|
||||
) => {
|
||||
compile_error!("`match_sharedterm!` used without a final wildcard match arm. You can just match on `shared_term.into_owned()`")
|
||||
};
|
||||
(
|
||||
match ($st: expr) {
|
||||
_ => $else_expr: expr $(,)?
|
||||
}
|
||||
) => {
|
||||
compile_error!("`match_sharedterm!` used with only a wildcard match arm. You can just unconditionally execute that arm")
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
|
@ -58,8 +58,7 @@ pub fn transform_one(rt: RichTerm) -> RichTerm {
|
||||
/// down traversal. This means that the return value is a normal `Term::Fun` but it can contain
|
||||
/// `Term::FunPattern` and `Term::LetPattern` inside.
|
||||
pub fn desugar_fun(rt: RichTerm) -> RichTerm {
|
||||
match_sharedterm! { rt.term,
|
||||
with {
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::FunPattern(x, pat, t_) => {
|
||||
let x = x.unwrap_or_else(LocIdent::fresh);
|
||||
let t_pos = t_.pos;
|
||||
@ -68,17 +67,14 @@ pub fn desugar_fun(rt: RichTerm) -> RichTerm {
|
||||
x,
|
||||
RichTerm::new(
|
||||
Term::LetPattern(None, pat, Term::Var(x).into(), t_),
|
||||
t_pos /* TODO: should we use rt.pos? */
|
||||
t_pos, /* TODO: should we use rt.pos? */
|
||||
),
|
||||
),
|
||||
rt.pos,
|
||||
)
|
||||
}
|
||||
}
|
||||
else {
|
||||
rt
|
||||
}
|
||||
}
|
||||
_ => rt,
|
||||
})
|
||||
}
|
||||
|
||||
/// Wrap the desugar `LetPattern` in a meta value containing the "Record contract" needed to check
|
||||
@ -86,8 +82,7 @@ pub fn desugar_fun(rt: RichTerm) -> RichTerm {
|
||||
/// record. This function should be, in the general case, considered as the entry point of the let
|
||||
/// patterns transformation.
|
||||
pub fn desugar_with_contract(rt: RichTerm) -> RichTerm {
|
||||
match_sharedterm!(rt.term,
|
||||
with {
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::LetPattern(x, pat, bound, body) => {
|
||||
let pos = body.pos;
|
||||
let contract = pat.clone().into_contract();
|
||||
@ -95,24 +90,29 @@ pub fn desugar_with_contract(rt: RichTerm) -> RichTerm {
|
||||
let t_pos = bound.pos;
|
||||
RichTerm::new(
|
||||
Term::Annotated(
|
||||
TypeAnnotation { contracts: vec![contract], ..Default::default() },
|
||||
bound
|
||||
TypeAnnotation {
|
||||
contracts: vec![contract],
|
||||
..Default::default()
|
||||
},
|
||||
bound,
|
||||
),
|
||||
t_pos,
|
||||
)
|
||||
};
|
||||
desugar(RichTerm::new(Term::LetPattern(x, pat, annotated, body), pos))
|
||||
desugar(RichTerm::new(
|
||||
Term::LetPattern(x, pat, annotated, body),
|
||||
pos,
|
||||
))
|
||||
}
|
||||
} else rt
|
||||
)
|
||||
_ => rt,
|
||||
})
|
||||
}
|
||||
|
||||
/// Main transformation function to desugar let patterns. WARNING: In a real usage case, you will
|
||||
/// want to generate also the contract associated to this pattern destructuring. Do not consider
|
||||
/// this function as the entry point of the transformation. For that, use `desugar_with_contract`.
|
||||
pub fn desugar(rt: RichTerm) -> RichTerm {
|
||||
match_sharedterm!(rt.term,
|
||||
with {
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::LetPattern(x, pat, t_, body) => {
|
||||
let pos = body.pos;
|
||||
let x = x.unwrap_or_else(LocIdent::fresh);
|
||||
@ -126,8 +126,8 @@ pub fn desugar(rt: RichTerm) -> RichTerm {
|
||||
pos,
|
||||
)
|
||||
}
|
||||
} else rt
|
||||
)
|
||||
_ => rt,
|
||||
})
|
||||
}
|
||||
|
||||
/// Wrap `body` in a let construct binding the open part of the pattern to the required value.
|
||||
|
@ -57,29 +57,52 @@ pub fn transform_one(rt: RichTerm) -> Result<RichTerm, UnboundTypeVariableError>
|
||||
}
|
||||
|
||||
let pos = rt.pos;
|
||||
let result = match_sharedterm! {rt.term,
|
||||
with {
|
||||
let result = match_sharedterm!(match (rt.term) {
|
||||
Term::RecRecord(record_data, dyn_fields, deps) => {
|
||||
let RecordData {fields, attrs, sealed_tail} = record_data;
|
||||
let RecordData {
|
||||
fields,
|
||||
attrs,
|
||||
sealed_tail,
|
||||
} = record_data;
|
||||
|
||||
let fields = attach_to_fields(fields)?;
|
||||
let dyn_fields = dyn_fields.into_iter().map(|(id_term, field)| {
|
||||
Ok((id_term, attach_to_field(field)?))
|
||||
}).collect::<Result<_, _>>()?;
|
||||
let dyn_fields = dyn_fields
|
||||
.into_iter()
|
||||
.map(|(id_term, field)| Ok((id_term, attach_to_field(field)?)))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
RichTerm::new(
|
||||
Term::RecRecord(RecordData {fields, attrs, sealed_tail}, dyn_fields, deps),
|
||||
pos
|
||||
)
|
||||
Term::RecRecord(
|
||||
RecordData {
|
||||
fields,
|
||||
attrs,
|
||||
sealed_tail,
|
||||
},
|
||||
dyn_fields,
|
||||
deps,
|
||||
),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
Term::Record(record_data) => {
|
||||
let RecordData {fields, attrs, sealed_tail} = record_data;
|
||||
let RecordData {
|
||||
fields,
|
||||
attrs,
|
||||
sealed_tail,
|
||||
} = record_data;
|
||||
|
||||
let fields = attach_to_fields(fields)?;
|
||||
|
||||
RichTerm::new(Term::Record(RecordData {fields, attrs, sealed_tail}), pos)
|
||||
RichTerm::new(
|
||||
Term::Record(RecordData {
|
||||
fields,
|
||||
attrs,
|
||||
sealed_tail,
|
||||
}),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
} else rt
|
||||
};
|
||||
_ => rt,
|
||||
});
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -53,8 +53,7 @@ struct Binding {
|
||||
/// traversal to obtain a full transformation.
|
||||
pub fn transform_one(rt: RichTerm) -> RichTerm {
|
||||
let pos = rt.pos;
|
||||
match_sharedterm! {rt.term,
|
||||
with {
|
||||
match_sharedterm!(match (rt.term) {
|
||||
// CAUTION: currently, if the record literals written by a user are always recursive
|
||||
// records, other phases of program transformation (such as desugaring of field paths
|
||||
// like `{foo.bar.baz = val}` or desugaring of destructuring) do introduce non
|
||||
@ -68,23 +67,41 @@ pub fn transform_one(rt: RichTerm) -> RichTerm {
|
||||
let mut bindings = Vec::with_capacity(record_data.fields.len());
|
||||
let empty_deps = FieldDeps::empty();
|
||||
|
||||
let fields = record_data.fields.into_iter().map(|(id, field)| {
|
||||
(id, transform_rec_field(field, Some(empty_deps.clone()), &mut bindings))
|
||||
}).collect();
|
||||
let fields = record_data
|
||||
.fields
|
||||
.into_iter()
|
||||
.map(|(id, field)| {
|
||||
(
|
||||
id,
|
||||
transform_rec_field(field, Some(empty_deps.clone()), &mut bindings),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
with_bindings(Term::Record(RecordData { fields, ..record_data }), bindings, pos)
|
||||
},
|
||||
with_bindings(
|
||||
Term::Record(RecordData {
|
||||
fields,
|
||||
..record_data
|
||||
}),
|
||||
bindings,
|
||||
pos,
|
||||
)
|
||||
}
|
||||
Term::RecRecord(record_data, dyn_fields, deps) => {
|
||||
let mut bindings = Vec::with_capacity(record_data.fields.len());
|
||||
|
||||
let fields = record_data.fields.into_iter().map(|(id, field)| {
|
||||
let fields = record_data
|
||||
.fields
|
||||
.into_iter()
|
||||
.map(|(id, field)| {
|
||||
let field_deps = deps
|
||||
.as_ref()
|
||||
.and_then(|deps| deps.stat_fields.get(&id.ident()))
|
||||
.cloned();
|
||||
|
||||
(id, transform_rec_field(field, field_deps, &mut bindings))
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
let dyn_fields = dyn_fields
|
||||
.into_iter()
|
||||
@ -99,11 +116,18 @@ pub fn transform_one(rt: RichTerm) -> RichTerm {
|
||||
.collect();
|
||||
|
||||
with_bindings(
|
||||
Term::RecRecord(RecordData { fields, ..record_data}, dyn_fields, deps),
|
||||
bindings,
|
||||
pos
|
||||
)
|
||||
Term::RecRecord(
|
||||
RecordData {
|
||||
fields,
|
||||
..record_data
|
||||
},
|
||||
dyn_fields,
|
||||
deps,
|
||||
),
|
||||
bindings,
|
||||
pos,
|
||||
)
|
||||
}
|
||||
Term::Array(ts, attrs) => {
|
||||
let mut bindings = Vec::with_capacity(ts.len());
|
||||
|
||||
@ -116,7 +140,7 @@ pub fn transform_one(rt: RichTerm) -> RichTerm {
|
||||
bindings.push(Binding {
|
||||
fresh_var,
|
||||
term: t,
|
||||
binding_type: BindingType::Normal
|
||||
binding_type: BindingType::Normal,
|
||||
});
|
||||
RichTerm::new(Term::Var(fresh_var), pos_t)
|
||||
} else {
|
||||
@ -126,15 +150,15 @@ pub fn transform_one(rt: RichTerm) -> RichTerm {
|
||||
.collect();
|
||||
|
||||
with_bindings(Term::Array(ts, attrs), bindings, pos)
|
||||
},
|
||||
}
|
||||
Term::Annotated(annot, t) if should_share(&t.term) => {
|
||||
let fresh_var = LocIdent::fresh();
|
||||
let shared = RichTerm::new(Term::Var(fresh_var), t.pos);
|
||||
let inner = RichTerm::new(Term::Annotated(annot, shared), pos);
|
||||
RichTerm::new(Term::Let(fresh_var, t, inner, LetAttrs::default()), pos)
|
||||
},
|
||||
} else rt
|
||||
}
|
||||
_ => rt,
|
||||
})
|
||||
}
|
||||
|
||||
/// Transform the field of a recursive record. Take care of transforming the pending contracts
|
||||
|
@ -21,17 +21,13 @@ use crate::{
|
||||
/// both the type annotation and the label's type with the inferred type.
|
||||
pub fn transform_one(rt: RichTerm, wildcards: &Wildcards) -> RichTerm {
|
||||
let pos = rt.pos;
|
||||
match_sharedterm! {rt.term,
|
||||
with {
|
||||
Term::Annotated(
|
||||
annot @ TypeAnnotation {
|
||||
typ: Some(_),
|
||||
..
|
||||
},
|
||||
inner
|
||||
) => {
|
||||
RichTerm::new(Term::Annotated(annot.subst_wildcards(wildcards), inner), pos)
|
||||
},
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::Annotated(annot @ TypeAnnotation { typ: Some(_), .. }, inner) => {
|
||||
RichTerm::new(
|
||||
Term::Annotated(annot.subst_wildcards(wildcards), inner),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
Term::RecRecord(record_data, dyn_fields, deps) => {
|
||||
let record_data = record_data.subst_wildcards(wildcards);
|
||||
let dyn_fields = dyn_fields
|
||||
@ -40,15 +36,12 @@ pub fn transform_one(rt: RichTerm, wildcards: &Wildcards) -> RichTerm {
|
||||
.collect();
|
||||
|
||||
RichTerm::new(Term::RecRecord(record_data, dyn_fields, deps), pos)
|
||||
},
|
||||
Term::Record(record_data) => {
|
||||
RichTerm::new(
|
||||
Term::Record(record_data.subst_wildcards(wildcards)),
|
||||
pos,
|
||||
)
|
||||
},
|
||||
} else rt
|
||||
}
|
||||
Term::Record(record_data) => {
|
||||
RichTerm::new(Term::Record(record_data.subst_wildcards(wildcards)), pos)
|
||||
}
|
||||
_ => rt,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the inferred type for a wildcard, or `Dyn` if no type was inferred.
|
||||
|
Loading…
Reference in New Issue
Block a user