mirror of
https://github.com/tweag/nickel.git
synced 2024-10-05 23:57:09 +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,25 +407,23 @@ 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,
|
||||
pos_record,
|
||||
pos_access: TermPos::None,
|
||||
}) => RichTerm::new(
|
||||
Term::RuntimeError(EvalError::MissingFieldDef {
|
||||
id,
|
||||
metadata,
|
||||
pos_record,
|
||||
pos_access: TermPos::None,
|
||||
}) => RichTerm::new(
|
||||
Term::RuntimeError(EvalError::MissingFieldDef {
|
||||
id,
|
||||
metadata,
|
||||
pos_record,
|
||||
pos_access: pos,
|
||||
}),
|
||||
pos,
|
||||
),
|
||||
} else {
|
||||
body
|
||||
}
|
||||
};
|
||||
pos_access: pos,
|
||||
}),
|
||||
pos,
|
||||
),
|
||||
_ => 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 {
|
||||
Term::Record(record) | Term::RecRecord(record, ..) => {
|
||||
let ext = record.fields.into_iter().filter_map(|(id, field)| {
|
||||
field.value.map(|value|
|
||||
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| {
|
||||
(
|
||||
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))
|
||||
}
|
||||
env.extend(ext);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(EnvBuildError::NotARecord(closure.body)),
|
||||
})
|
||||
}
|
||||
|
||||
/// Bind a closure in an environment.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -474,36 +474,36 @@ impl<EC: EvalCache> Program<EC> {
|
||||
_ => result,
|
||||
}?;
|
||||
|
||||
match_sharedterm! {rt.term, with {
|
||||
Term::Record(data) => {
|
||||
let fields = data
|
||||
.fields
|
||||
.into_iter()
|
||||
.map(|(id, field)| -> Result<_, Error> {
|
||||
Ok((
|
||||
id,
|
||||
Field {
|
||||
value: field
|
||||
.value
|
||||
.map(|rt| do_eval(vm, rt, env.clone()))
|
||||
.transpose()?,
|
||||
pending_contracts: eval_contracts(
|
||||
vm,
|
||||
field.pending_contracts,
|
||||
env.clone(),
|
||||
)?,
|
||||
..field
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, Error>>()?;
|
||||
Ok(RichTerm::new(
|
||||
Term::Record(RecordData { fields, ..data }),
|
||||
rt.pos,
|
||||
))
|
||||
}
|
||||
} else Ok(rt)
|
||||
}
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::Record(data) => {
|
||||
let fields = data
|
||||
.fields
|
||||
.into_iter()
|
||||
.map(|(id, field)| -> Result<_, Error> {
|
||||
Ok((
|
||||
id,
|
||||
Field {
|
||||
value: field
|
||||
.value
|
||||
.map(|rt| do_eval(vm, rt, env.clone()))
|
||||
.transpose()?,
|
||||
pending_contracts: eval_contracts(
|
||||
vm,
|
||||
field.pending_contracts,
|
||||
env.clone(),
|
||||
)?,
|
||||
..field
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, Error>>()?;
|
||||
Ok(RichTerm::new(
|
||||
Term::Record(RecordData { fields, ..data }),
|
||||
rt.pos,
|
||||
))
|
||||
}
|
||||
_ => 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()
|
||||
.map(|t| t.traverse(f, order))
|
||||
.collect::<Result<Rc<[_]>, _>>()?);
|
||||
let ts_res = Array::new(
|
||||
ts.into_iter()
|
||||
.map(|t| t.traverse(f, order))
|
||||
.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,27 +58,23 @@ 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 {
|
||||
Term::FunPattern(x, pat, t_) => {
|
||||
let x = x.unwrap_or_else(LocIdent::fresh);
|
||||
let t_pos = t_.pos;
|
||||
RichTerm::new(
|
||||
Term::Fun(
|
||||
x,
|
||||
RichTerm::new(
|
||||
Term::LetPattern(None, pat, Term::Var(x).into(), t_),
|
||||
t_pos /* TODO: should we use rt.pos? */
|
||||
),
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::FunPattern(x, pat, t_) => {
|
||||
let x = x.unwrap_or_else(LocIdent::fresh);
|
||||
let t_pos = t_.pos;
|
||||
RichTerm::new(
|
||||
Term::Fun(
|
||||
x,
|
||||
RichTerm::new(
|
||||
Term::LetPattern(None, pat, Term::Var(x).into(), t_),
|
||||
t_pos, /* TODO: should we use rt.pos? */
|
||||
),
|
||||
rt.pos,
|
||||
)
|
||||
}
|
||||
),
|
||||
rt.pos,
|
||||
)
|
||||
}
|
||||
else {
|
||||
rt
|
||||
}
|
||||
}
|
||||
_ => rt,
|
||||
})
|
||||
}
|
||||
|
||||
/// Wrap the desugar `LetPattern` in a meta value containing the "Record contract" needed to check
|
||||
@ -86,48 +82,52 @@ 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 {
|
||||
Term::LetPattern(x, pat, bound, body) => {
|
||||
let pos = body.pos;
|
||||
let contract = pat.clone().into_contract();
|
||||
let annotated = {
|
||||
let t_pos = bound.pos;
|
||||
RichTerm::new(
|
||||
Term::Annotated(
|
||||
TypeAnnotation { contracts: vec![contract], ..Default::default() },
|
||||
bound
|
||||
),
|
||||
t_pos,
|
||||
)
|
||||
};
|
||||
desugar(RichTerm::new(Term::LetPattern(x, pat, annotated, body), pos))
|
||||
}
|
||||
} else rt
|
||||
)
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::LetPattern(x, pat, bound, body) => {
|
||||
let pos = body.pos;
|
||||
let contract = pat.clone().into_contract();
|
||||
let annotated = {
|
||||
let t_pos = bound.pos;
|
||||
RichTerm::new(
|
||||
Term::Annotated(
|
||||
TypeAnnotation {
|
||||
contracts: vec![contract],
|
||||
..Default::default()
|
||||
},
|
||||
bound,
|
||||
),
|
||||
t_pos,
|
||||
)
|
||||
};
|
||||
desugar(RichTerm::new(
|
||||
Term::LetPattern(x, pat, annotated, body),
|
||||
pos,
|
||||
))
|
||||
}
|
||||
_ => 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 {
|
||||
Term::LetPattern(x, pat, t_, body) => {
|
||||
let pos = body.pos;
|
||||
let x = x.unwrap_or_else(LocIdent::fresh);
|
||||
RichTerm::new(
|
||||
Term::Let(
|
||||
x,
|
||||
t_,
|
||||
destruct_term(x, &pat, bind_open_field(x, &pat, body)),
|
||||
Default::default(),
|
||||
),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
} else rt
|
||||
)
|
||||
match_sharedterm!(match (rt.term) {
|
||||
Term::LetPattern(x, pat, t_, body) => {
|
||||
let pos = body.pos;
|
||||
let x = x.unwrap_or_else(LocIdent::fresh);
|
||||
RichTerm::new(
|
||||
Term::Let(
|
||||
x,
|
||||
t_,
|
||||
destruct_term(x, &pat, bind_open_field(x, &pat, body)),
|
||||
Default::default(),
|
||||
),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
_ => 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 {
|
||||
Term::RecRecord(record_data, dyn_fields, deps) => {
|
||||
let RecordData {fields, attrs, sealed_tail} = record_data;
|
||||
let result = match_sharedterm!(match (rt.term) {
|
||||
Term::RecRecord(record_data, dyn_fields, deps) => {
|
||||
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 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<_, _>>()?;
|
||||
|
||||
RichTerm::new(
|
||||
Term::RecRecord(RecordData {fields, attrs, sealed_tail}, dyn_fields, deps),
|
||||
pos
|
||||
)
|
||||
},
|
||||
Term::Record(record_data) => {
|
||||
let RecordData {fields, attrs, sealed_tail} = record_data;
|
||||
RichTerm::new(
|
||||
Term::RecRecord(
|
||||
RecordData {
|
||||
fields,
|
||||
attrs,
|
||||
sealed_tail,
|
||||
},
|
||||
dyn_fields,
|
||||
deps,
|
||||
),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
Term::Record(record_data) => {
|
||||
let RecordData {
|
||||
fields,
|
||||
attrs,
|
||||
sealed_tail,
|
||||
} = record_data;
|
||||
|
||||
let fields = attach_to_fields(fields)?;
|
||||
let fields = attach_to_fields(fields)?;
|
||||
|
||||
RichTerm::new(Term::Record(RecordData {fields, attrs, sealed_tail}), pos)
|
||||
}
|
||||
} else rt
|
||||
};
|
||||
RichTerm::new(
|
||||
Term::Record(RecordData {
|
||||
fields,
|
||||
attrs,
|
||||
sealed_tail,
|
||||
}),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
_ => rt,
|
||||
});
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -53,88 +53,112 @@ struct Binding {
|
||||
/// traversal to obtain a full transformation.
|
||||
pub fn transform_one(rt: RichTerm) -> RichTerm {
|
||||
let pos = rt.pos;
|
||||
match_sharedterm! {rt.term,
|
||||
with {
|
||||
// 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
|
||||
// recursive records.
|
||||
//
|
||||
// I've scratched my head more that once on a transformation seemingly not happening
|
||||
// only for record elaborated by the destructuring desguaring phase, to find that the
|
||||
// `Term::Record` case below has been forgotten, and only the case `Term::RecRecord`
|
||||
// was updated.
|
||||
Term::Record(record_data) => {
|
||||
let mut bindings = Vec::with_capacity(record_data.fields.len());
|
||||
let empty_deps = FieldDeps::empty();
|
||||
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
|
||||
// recursive records.
|
||||
//
|
||||
// I've scratched my head more that once on a transformation seemingly not happening
|
||||
// only for record elaborated by the destructuring desguaring phase, to find that the
|
||||
// `Term::Record` case below has been forgotten, and only the case `Term::RecRecord`
|
||||
// was updated.
|
||||
Term::Record(record_data) => {
|
||||
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)
|
||||
},
|
||||
Term::RecRecord(record_data, dyn_fields, deps) => {
|
||||
let mut bindings = Vec::with_capacity(record_data.fields.len());
|
||||
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()
|
||||
.enumerate()
|
||||
.map(|(index, (id_t, field))| {
|
||||
let field_deps = deps
|
||||
.as_ref()
|
||||
.and_then(|deps| deps.dyn_fields.get(index))
|
||||
.cloned();
|
||||
(id_t, transform_rec_field(field, field_deps, &mut bindings))
|
||||
})
|
||||
.collect();
|
||||
let dyn_fields = dyn_fields
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (id_t, field))| {
|
||||
let field_deps = deps
|
||||
.as_ref()
|
||||
.and_then(|deps| deps.dyn_fields.get(index))
|
||||
.cloned();
|
||||
(id_t, transform_rec_field(field, field_deps, &mut bindings))
|
||||
})
|
||||
.collect();
|
||||
|
||||
with_bindings(
|
||||
Term::RecRecord(RecordData { fields, ..record_data}, dyn_fields, deps),
|
||||
bindings,
|
||||
pos
|
||||
)
|
||||
},
|
||||
Term::Array(ts, attrs) => {
|
||||
let mut bindings = Vec::with_capacity(ts.len());
|
||||
with_bindings(
|
||||
Term::RecRecord(
|
||||
RecordData {
|
||||
fields,
|
||||
..record_data
|
||||
},
|
||||
dyn_fields,
|
||||
deps,
|
||||
),
|
||||
bindings,
|
||||
pos,
|
||||
)
|
||||
}
|
||||
Term::Array(ts, attrs) => {
|
||||
let mut bindings = Vec::with_capacity(ts.len());
|
||||
|
||||
let ts = ts
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
if should_share(&t.term) {
|
||||
let fresh_var = LocIdent::fresh();
|
||||
let pos_t = t.pos;
|
||||
bindings.push(Binding {
|
||||
fresh_var,
|
||||
term: t,
|
||||
binding_type: BindingType::Normal
|
||||
});
|
||||
RichTerm::new(Term::Var(fresh_var), pos_t)
|
||||
} else {
|
||||
t
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let ts = ts
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
if should_share(&t.term) {
|
||||
let fresh_var = LocIdent::fresh();
|
||||
let pos_t = t.pos;
|
||||
bindings.push(Binding {
|
||||
fresh_var,
|
||||
term: t,
|
||||
binding_type: BindingType::Normal,
|
||||
});
|
||||
RichTerm::new(Term::Var(fresh_var), pos_t)
|
||||
} else {
|
||||
t
|
||||
}
|
||||
})
|
||||
.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
|
||||
}
|
||||
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)
|
||||
}
|
||||
_ => rt,
|
||||
})
|
||||
}
|
||||
|
||||
/// Transform the field of a recursive record. Take care of transforming the pending contracts
|
||||
|
@ -21,34 +21,27 @@ 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)
|
||||
},
|
||||
Term::RecRecord(record_data, dyn_fields, deps) => {
|
||||
let record_data = record_data.subst_wildcards(wildcards);
|
||||
let dyn_fields = dyn_fields
|
||||
.into_iter()
|
||||
.map(|(id_t, field)| (id_t, field.subst_wildcards(wildcards)))
|
||||
.collect();
|
||||
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
|
||||
.into_iter()
|
||||
.map(|(id_t, field)| (id_t, field.subst_wildcards(wildcards)))
|
||||
.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
|
||||
}
|
||||
RichTerm::new(Term::RecRecord(record_data, dyn_fields, deps), pos)
|
||||
}
|
||||
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