@@ -134,10 +134,12 @@ fn is_type_var(n: &str) -> bool {
136/// A compiled signature: a chain of parameter types and a final result type.
136/// 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.
137#[derive(Clone, Debug)]
139#[derive(Clone, Debug)]
138pub struct Contract {
140pub struct Contract {
139− pub params: Vec<TypeExpr>,
141+ pub params: Vec<Rc2<TypeExpr>>,
140− pub result: TypeExpr,
142+ pub result: Rc2<TypeExpr>,
143pub fn compile_contract(shapes: &Shapes, ty: &TypeExpr) -> Contract {
145pub fn compile_contract(shapes: &Shapes, ty: &TypeExpr) -> Contract {
@@ -146,7 +148,7 @@ pub fn compile_contract(shapes: &Shapes, ty: &TypeExpr) -> Contract {
147 match cur {
149 match cur {
148 TypeExpr::Fun(a, b) => {
150 TypeExpr::Fun(a, b) => {
149− params.push((**a).clone());
151+ params.push(a.clone());
152 TypeExpr::Con(n) => {
154 TypeExpr::Con(n) => {
@@ -164,7 +166,7 @@ pub fn compile_contract(shapes: &Shapes, ty: &TypeExpr) -> Contract {
165 Contract {
167 Contract {
167− result: cur.clone(),
169+ result: Rc2::new(cur.clone()),
@@ -275,7 +277,10 @@ use std::rc::Rc as Rc2;
275pub enum ContractExpr {
277pub enum ContractExpr {
276 /// no contract information; arg checks pass through
278 /// no contract information; arg checks pass through
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
278− Known(Rc2<Contract>),
283+ Known { contract: Rc2<Contract>, at: usize },
279 /// compose(f, g): (f . g); both contract expressions
284 /// compose(f, g): (f . g); both contract expressions
280 Compose(Box<ContractExpr>, Box<ContractExpr>),
285 Compose(Box<ContractExpr>, Box<ContractExpr>),
281 /// apply e to an argument that checked against e's first parameter
286 /// apply e to an argument that checked against e's first parameter
@@ -284,10 +289,10 @@ pub enum ContractExpr {
285impl ContractExpr {
290impl ContractExpr {
286 /// The type of the next parameter this function expects, if known.
291 /// 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>> {
288 match self {
293 match self {
289 ContractExpr::Unknown => None,
294 ContractExpr::Unknown => None,
290− ContractExpr::Known(c) => c.params.first().cloned(),
295+ ContractExpr::Known { contract, at } => contract.params.get(*at).cloned(),
291 ContractExpr::Compose(f, g) => g
296 ContractExpr::Compose(f, g) => g
292 .next_param()
297 .next_param()
293 .or_else(|| f.next_param())
298 .or_else(|| f.next_param())
@@ -301,12 +306,12 @@ impl ContractExpr {
303 /// The result type once all parameters are supplied.
308 /// 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>> {
305 match self {
310 match self {
306 ContractExpr::Unknown => None,
311 ContractExpr::Unknown => None,
307− ContractExpr::Known(c) => {
312+ ContractExpr::Known { contract, at } => {
308− if c.params.is_empty() {
313+ if *at >= contract.params.len() {
309− Some(c.result.clone())
314+ Some(contract.result.clone())
@@ -326,7 +331,7 @@ impl ContractExpr {
326 pub fn is_exhausted(&self) -> bool {
331 pub fn is_exhausted(&self) -> bool {
327 match self {
332 match self {
328 ContractExpr::Unknown => false,
333 ContractExpr::Unknown => false,
329− ContractExpr::Known(c) => c.params.is_empty(),
334+ ContractExpr::Known { contract, at } => *at >= contract.params.len(),
330 ContractExpr::Compose(_, g) => g.is_exhausted(),
335 ContractExpr::Compose(_, g) => g.is_exhausted(),
331 ContractExpr::ApplyFirst(_) => false,
336 ContractExpr::ApplyFirst(_) => false,
@@ -334,19 +339,24 @@ impl ContractExpr {
335 /// Advance after one argument has been supplied and checked.
340 /// Advance after one argument has been supplied and checked.
336 pub fn apply_first_rc(self: Rc2<ContractExpr>) -> ContractExpr {
341 pub fn apply_first_rc(self: Rc2<ContractExpr>) -> ContractExpr {
342+ // the common case advances without allocating anything
344+ ContractExpr::Unknown => ContractExpr::Unknown,
345+ ContractExpr::Known { contract, at } => ContractExpr::Known {
346+ contract: contract.clone(),
347+ at: contract.params.len().min(at + 1),
337− ContractExpr::apply_first(Box::new((*self).clone()))
349+ _ => ContractExpr::apply_first(Box::new((*self).clone())),
340 /// Advance after one argument has been supplied and checked.
353 /// Advance after one argument has been supplied and checked.
341 pub fn apply_first(self: Box<ContractExpr>) -> ContractExpr {
354 pub fn apply_first(self: Box<ContractExpr>) -> ContractExpr {
342 match *self {
355 match *self {
343 ContractExpr::Unknown => ContractExpr::Unknown,
356 ContractExpr::Unknown => ContractExpr::Unknown,
344− ContractExpr::Known(c) => {
357+ ContractExpr::Known { contract, at } => {
345− let mut c2 = (*c).clone();
358+ let at = contract.params.len().min(at + 1);
346− if !c2.params.is_empty() {
347− c2.params.remove(0);
349− ContractExpr::Known(Rc2::new(c2))
359+ ContractExpr::Known { contract, at }
351 ContractExpr::Compose(f, g) => {
361 ContractExpr::Compose(f, g) => {
352 if !g.is_exhausted() {
362 if !g.is_exhausted() {
@@ -363,7 +373,7 @@ impl ContractExpr {
363 /// Number of remaining parameters, if known and not nested.
373 /// Number of remaining parameters, if known and not nested.
364 pub fn params_len(&self) -> usize {
374 pub fn params_len(&self) -> usize {
365 match self {
375 match self {
366− ContractExpr::Known(c) => c.params.len(),
376+ ContractExpr::Known { contract, at } => contract.params.len() - at,