1
1
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:
Taeer Bar-Yam 2023-10-13 12:46:12 -04:00 committed by GitHub
parent 7e4ecd908c
commit 859293a3e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1008 additions and 989 deletions

View File

@ -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

View File

@ -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())

View File

@ -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]

View File

@ -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.

View File

@ -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)
}

View File

@ -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

View File

@ -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.