@@ -134,10 +134,12 @@ fn is_type_var(n: &str) -> bool {
134134}
135135
136136/// A compiled signature: a chain of parameter types and a final result type.
137+/// The types are shared, so handing one out is a refcount bump rather than a
138+/// deep copy — contracts are consulted on every call.
137139#[derive(Clone, Debug)]
138140pub struct Contract {
139− pub params: Vec<TypeExpr>,
140− pub result: TypeExpr,
141+ pub params: Vec<Rc2<TypeExpr>>,
142+ pub result: Rc2<TypeExpr>,
141143}
142144
143145pub fn compile_contract(shapes: &Shapes, ty: &TypeExpr) -> Contract {
@@ -146,7 +148,7 @@ pub fn compile_contract(shapes: &Shapes, ty: &TypeExpr) -> Contract {
146148 loop {
147149 match cur {
148150 TypeExpr::Fun(a, b) => {
149− params.push((**a).clone());
151+ params.push(a.clone());
150152 cur = b;
151153 }
152154 TypeExpr::Con(n) => {
@@ -164,7 +166,7 @@ pub fn compile_contract(shapes: &Shapes, ty: &TypeExpr) -> Contract {
164166 }
165167 Contract {
166168 params,
167− result: cur.clone(),
169+ result: Rc2::new(cur.clone()),
168170 }
169171}
170172
@@ -275,7 +277,10 @@ use std::rc::Rc as Rc2;
275277pub enum ContractExpr {
276278 /// no contract information; arg checks pass through
277279 Unknown,
278− Known(Rc2<Contract>),
280+ /// a signature with its first `at` parameters already supplied; advancing
281+ /// it is an index bump, where rebuilding the shortened parameter list
282+ /// copied every remaining type on every application
283+ Known { contract: Rc2<Contract>, at: usize },
279284 /// compose(f, g): (f . g); both contract expressions
280285 Compose(Box<ContractExpr>, Box<ContractExpr>),
281286 /// apply e to an argument that checked against e's first parameter
@@ -284,10 +289,10 @@ pub enum ContractExpr {
284289
285290impl ContractExpr {
286291 /// The type of the next parameter this function expects, if known.
287− pub fn next_param(&self) -> Option<TypeExpr> {
292+ pub fn next_param(&self) -> Option<Rc2<TypeExpr>> {
288293 match self {
289294 ContractExpr::Unknown => None,
290− ContractExpr::Known(c) => c.params.first().cloned(),
295+ ContractExpr::Known { contract, at } => contract.params.get(*at).cloned(),
291296 ContractExpr::Compose(f, g) => g
292297 .next_param()
293298 .or_else(|| f.next_param())
@@ -301,12 +306,12 @@ impl ContractExpr {
301306 }
302307
303308 /// The result type once all parameters are supplied.
304− pub fn result_after(&self) -> Option<TypeExpr> {
309+ pub fn result_after(&self) -> Option<Rc2<TypeExpr>> {
305310 match self {
306311 ContractExpr::Unknown => None,
307− ContractExpr::Known(c) => {
308− if c.params.is_empty() {
309− Some(c.result.clone())
312+ ContractExpr::Known { contract, at } => {
313+ if *at >= contract.params.len() {
314+ Some(contract.result.clone())
310315 } else {
311316 None
312317 }
@@ -326,7 +331,7 @@ impl ContractExpr {
326331 pub fn is_exhausted(&self) -> bool {
327332 match self {
328333 ContractExpr::Unknown => false,
329− ContractExpr::Known(c) => c.params.is_empty(),
334+ ContractExpr::Known { contract, at } => *at >= contract.params.len(),
330335 ContractExpr::Compose(_, g) => g.is_exhausted(),
331336 ContractExpr::ApplyFirst(_) => false,
332337 }
@@ -334,19 +339,24 @@ impl ContractExpr {
334339
335340 /// Advance after one argument has been supplied and checked.
336341 pub fn apply_first_rc(self: Rc2<ContractExpr>) -> ContractExpr {
337− ContractExpr::apply_first(Box::new((*self).clone()))
342+ // the common case advances without allocating anything
343+ match &*self {
344+ ContractExpr::Unknown => ContractExpr::Unknown,
345+ ContractExpr::Known { contract, at } => ContractExpr::Known {
346+ contract: contract.clone(),
347+ at: contract.params.len().min(at + 1),
348+ },
349+ _ => ContractExpr::apply_first(Box::new((*self).clone())),
350+ }
338351 }
339352
340353 /// Advance after one argument has been supplied and checked.
341354 pub fn apply_first(self: Box<ContractExpr>) -> ContractExpr {
342355 match *self {
343356 ContractExpr::Unknown => ContractExpr::Unknown,
344− ContractExpr::Known(c) => {
345− let mut c2 = (*c).clone();
346− if !c2.params.is_empty() {
347− c2.params.remove(0);
348− }
349− ContractExpr::Known(Rc2::new(c2))
357+ ContractExpr::Known { contract, at } => {
358+ let at = contract.params.len().min(at + 1);
359+ ContractExpr::Known { contract, at }
350360 }
351361 ContractExpr::Compose(f, g) => {
352362 if !g.is_exhausted() {
@@ -363,7 +373,7 @@ impl ContractExpr {
363373 /// Number of remaining parameters, if known and not nested.
364374 pub fn params_len(&self) -> usize {
365375 match self {
366− ContractExpr::Known(c) => c.params.len(),
376+ ContractExpr::Known { contract, at } => contract.params.len() - at,
367377 _ => 99,
368378 }
369379 }