diff src/elaborate.sml @ 403:8084fa9216de

New implicit argument handling
author Adam Chlipala <adamc@hcoop.net>
date Tue, 21 Oct 2008 16:41:11 -0400
parents ebf27030ae3b
children 06fcddcd20d3
line wrap: on
line diff
--- a/src/elaborate.sml	Tue Oct 21 15:11:42 2008 -0400
+++ b/src/elaborate.sml	Tue Oct 21 16:41:11 2008 -0400
@@ -1,1041 +1,1057 @@
-(* Copyright (c) 2008, Adam Chlipala
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * - The names of contributors may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *)
-
-structure Elaborate :> ELABORATE = struct
-
-structure P = Prim
-structure L = Source
-structure L' = Elab
-structure E = ElabEnv
-structure U = ElabUtil
-structure D = Disjoint
-
-open Print
-open ElabPrint
-open ElabErr
-
-structure IM = IntBinaryMap
-
-structure SK = struct
-type ord_key = string
-val compare = String.compare
-end
-
-structure SS = BinarySetFn(SK)
-structure SM = BinaryMapFn(SK)
-
-val basis_r = ref 0
-
-fun elabExplicitness e =
-    case e of
-        L.Explicit => L'.Explicit
-      | L.Implicit => L'.Implicit
-
-fun occursKind r =
-    U.Kind.exists (fn L'.KUnif (_, _, r') => r = r'
-                    | _ => false)
-
-exception KUnify' of kunify_error
-
-fun unifyKinds' (k1All as (k1, _)) (k2All as (k2, _)) =
-    let
-        fun err f = raise KUnify' (f (k1All, k2All))
-    in
-        case (k1, k2) of
-            (L'.KType, L'.KType) => ()
-          | (L'.KUnit, L'.KUnit) => ()
-
-          | (L'.KArrow (d1, r1), L'.KArrow (d2, r2)) =>
-            (unifyKinds' d1 d2;
-             unifyKinds' r1 r2)
-          | (L'.KName, L'.KName) => ()
-          | (L'.KRecord k1, L'.KRecord k2) => unifyKinds' k1 k2
-          | (L'.KTuple ks1, L'.KTuple ks2) =>
-            ((ListPair.appEq (fn (k1, k2) => unifyKinds' k1 k2) (ks1, ks2))
-             handle ListPair.UnequalLengths => err KIncompatible)
-
-          | (L'.KError, _) => ()
-          | (_, L'.KError) => ()
-
-          | (L'.KUnif (_, _, ref (SOME k1All)), _) => unifyKinds' k1All k2All
-          | (_, L'.KUnif (_, _, ref (SOME k2All))) => unifyKinds' k1All k2All
-
-          | (L'.KUnif (_, _, r1), L'.KUnif (_, _, r2)) =>
-            if r1 = r2 then
-                ()
-            else
-                r1 := SOME k2All
-
-          | (L'.KUnif (_, _, r), _) =>
-            if occursKind r k2All then
-                err KOccursCheckFailed
-            else
-                r := SOME k2All
-          | (_, L'.KUnif (_, _, r)) =>
-            if occursKind r k1All then
-                err KOccursCheckFailed
-            else
-                r := SOME k1All
-
-          | _ => err KIncompatible
-    end
-
-exception KUnify of L'.kind * L'.kind * kunify_error
-
-fun unifyKinds k1 k2 =
-    unifyKinds' k1 k2
-    handle KUnify' err => raise KUnify (k1, k2, err)
-
-fun checkKind env c k1 k2 =
-    unifyKinds k1 k2
-    handle KUnify (k1, k2, err) =>
-           conError env (WrongKind (c, k1, k2, err))
-
-val dummy = ErrorMsg.dummySpan
-
-val ktype = (L'.KType, dummy)
-val kname = (L'.KName, dummy)
-val ktype_record = (L'.KRecord ktype, dummy)
-
-val cerror = (L'.CError, dummy)
-val kerror = (L'.KError, dummy)
-val eerror = (L'.EError, dummy)
-val sgnerror = (L'.SgnError, dummy)
-val strerror = (L'.StrError, dummy)
-
-val int = ref cerror
-val float = ref cerror
-val string = ref cerror
-val table = ref cerror
-
-local
-    val count = ref 0
-in
-
-fun resetKunif () = count := 0
-
-fun kunif loc =
-    let
-        val n = !count
-        val s = if n <= 26 then
-                    str (chr (ord #"A" + n))
-                else
-                    "U" ^ Int.toString (n - 26)
-    in
-        count := n + 1;
-        (L'.KUnif (loc, s, ref NONE), dummy)
-    end
-
-end
-
-local
-    val count = ref 0
-in
-
-fun resetCunif () = count := 0
-
-fun cunif (loc, k) =
-    let
-        val n = !count
-        val s = if n <= 26 then
-                    str (chr (ord #"A" + n))
-                else
-                    "U" ^ Int.toString (n - 26)
-    in
-        count := n + 1;
-        (L'.CUnif (loc, k, s, ref NONE), dummy)
-    end
-
-end
-
-fun elabKind (k, loc) =
-    case k of
-        L.KType => (L'.KType, loc)
-      | L.KArrow (k1, k2) => (L'.KArrow (elabKind k1, elabKind k2), loc)
-      | L.KName => (L'.KName, loc)
-      | L.KRecord k => (L'.KRecord (elabKind k), loc)
-      | L.KUnit => (L'.KUnit, loc)
-      | L.KTuple ks => (L'.KTuple (map elabKind ks), loc)
-      | L.KWild => kunif loc
-
-fun foldKind (dom, ran, loc)=
-    (L'.KArrow ((L'.KArrow ((L'.KName, loc),
-                            (L'.KArrow (dom,
-                                        (L'.KArrow (ran, ran), loc)), loc)), loc),
-                (L'.KArrow (ran,
-                            (L'.KArrow ((L'.KRecord dom, loc),
-                                        ran), loc)), loc)), loc)
-
-fun hnormKind (kAll as (k, _)) =
-    case k of
-        L'.KUnif (_, _, ref (SOME k)) => hnormKind k
-      | _ => kAll
-
-fun elabCon (env, denv) (c, loc) =
-    case c of
-        L.CAnnot (c, k) =>
-        let
-            val k' = elabKind k
-            val (c', ck, gs) = elabCon (env, denv) c
-        in
-            checkKind env c' ck k';
-            (c', k', gs)
-        end
-
-      | L.TFun (t1, t2) =>
-        let
-            val (t1', k1, gs1) = elabCon (env, denv) t1
-            val (t2', k2, gs2) = elabCon (env, denv) t2
-        in
-            checkKind env t1' k1 ktype;
-            checkKind env t2' k2 ktype;
-            ((L'.TFun (t1', t2'), loc), ktype, gs1 @ gs2)
-        end
-      | L.TCFun (e, x, k, t) =>
-        let
-            val e' = elabExplicitness e
-            val k' = elabKind k
-            val env' = E.pushCRel env x k'
-            val (t', tk, gs) = elabCon (env', D.enter denv) t
-        in
-            checkKind env t' tk ktype;
-            ((L'.TCFun (e', x, k', t'), loc), ktype, gs)
-        end
-      | L.CDisjoint (c1, c2, c) =>
-        let
-            val (c1', k1, gs1) = elabCon (env, denv) c1
-            val (c2', k2, gs2) = elabCon (env, denv) c2
-
-            val ku1 = kunif loc
-            val ku2 = kunif loc
-
-            val (denv', gs3) = D.assert env denv (c1', c2')
-            val (c', k, gs4) = elabCon (env, denv') c
-        in
-            checkKind env c1' k1 (L'.KRecord ku1, loc);
-            checkKind env c2' k2 (L'.KRecord ku2, loc);
-
-            ((L'.CDisjoint (L'.Instantiate, c1', c2', c'), loc), k, gs1 @ gs2 @ gs3 @ gs4)
-        end
-      | L.TRecord c =>
-        let
-            val (c', ck, gs) = elabCon (env, denv) c
-            val k = (L'.KRecord ktype, loc)
-        in
-            checkKind env c' ck k;
-            ((L'.TRecord c', loc), ktype, gs)
-        end
-
-      | L.CVar ([], s) =>
-        (case E.lookupC env s of
-             E.NotBound =>
-             (conError env (UnboundCon (loc, s));
-              (cerror, kerror, []))
-           | E.Rel (n, k) =>
-             ((L'.CRel n, loc), k, [])
-           | E.Named (n, k) =>
-             ((L'.CNamed n, loc), k, []))
-      | L.CVar (m1 :: ms, s) =>
-        (case E.lookupStr env m1 of
-             NONE => (conError env (UnboundStrInCon (loc, m1));
+ (* Copyright (c) 2008, Adam Chlipala
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions are met:
+  *
+  * - Redistributions of source code must retain the above copyright notice,
+  *   this list of conditions and the following disclaimer.
+  * - Redistributions in binary form must reproduce the above copyright notice,
+  *   this list of conditions and the following disclaimer in the documentation
+  *   and/or other materials provided with the distribution.
+  * - The names of contributors may not be used to endorse or promote products
+  *   derived from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *)
+
+ structure Elaborate :> ELABORATE = struct
+
+ structure P = Prim
+ structure L = Source
+ structure L' = Elab
+ structure E = ElabEnv
+ structure U = ElabUtil
+ structure D = Disjoint
+
+ open Print
+ open ElabPrint
+ open ElabErr
+
+ structure IM = IntBinaryMap
+
+ structure SK = struct
+ type ord_key = string
+ val compare = String.compare
+ end
+
+ structure SS = BinarySetFn(SK)
+ structure SM = BinaryMapFn(SK)
+
+ val basis_r = ref 0
+
+ fun elabExplicitness e =
+     case e of
+         L.Explicit => L'.Explicit
+       | L.Implicit => L'.Implicit
+
+ fun occursKind r =
+     U.Kind.exists (fn L'.KUnif (_, _, r') => r = r'
+                     | _ => false)
+
+ exception KUnify' of kunify_error
+
+ fun unifyKinds' (k1All as (k1, _)) (k2All as (k2, _)) =
+     let
+         fun err f = raise KUnify' (f (k1All, k2All))
+     in
+         case (k1, k2) of
+             (L'.KType, L'.KType) => ()
+           | (L'.KUnit, L'.KUnit) => ()
+
+           | (L'.KArrow (d1, r1), L'.KArrow (d2, r2)) =>
+             (unifyKinds' d1 d2;
+              unifyKinds' r1 r2)
+           | (L'.KName, L'.KName) => ()
+           | (L'.KRecord k1, L'.KRecord k2) => unifyKinds' k1 k2
+           | (L'.KTuple ks1, L'.KTuple ks2) =>
+             ((ListPair.appEq (fn (k1, k2) => unifyKinds' k1 k2) (ks1, ks2))
+              handle ListPair.UnequalLengths => err KIncompatible)
+
+           | (L'.KError, _) => ()
+           | (_, L'.KError) => ()
+
+           | (L'.KUnif (_, _, ref (SOME k1All)), _) => unifyKinds' k1All k2All
+           | (_, L'.KUnif (_, _, ref (SOME k2All))) => unifyKinds' k1All k2All
+
+           | (L'.KUnif (_, _, r1), L'.KUnif (_, _, r2)) =>
+             if r1 = r2 then
+                 ()
+             else
+                 r1 := SOME k2All
+
+           | (L'.KUnif (_, _, r), _) =>
+             if occursKind r k2All then
+                 err KOccursCheckFailed
+             else
+                 r := SOME k2All
+           | (_, L'.KUnif (_, _, r)) =>
+             if occursKind r k1All then
+                 err KOccursCheckFailed
+             else
+                 r := SOME k1All
+
+           | _ => err KIncompatible
+     end
+
+ exception KUnify of L'.kind * L'.kind * kunify_error
+
+ fun unifyKinds k1 k2 =
+     unifyKinds' k1 k2
+     handle KUnify' err => raise KUnify (k1, k2, err)
+
+ fun checkKind env c k1 k2 =
+     unifyKinds k1 k2
+     handle KUnify (k1, k2, err) =>
+            conError env (WrongKind (c, k1, k2, err))
+
+ val dummy = ErrorMsg.dummySpan
+
+ val ktype = (L'.KType, dummy)
+ val kname = (L'.KName, dummy)
+ val ktype_record = (L'.KRecord ktype, dummy)
+
+ val cerror = (L'.CError, dummy)
+ val kerror = (L'.KError, dummy)
+ val eerror = (L'.EError, dummy)
+ val sgnerror = (L'.SgnError, dummy)
+ val strerror = (L'.StrError, dummy)
+
+ val int = ref cerror
+ val float = ref cerror
+ val string = ref cerror
+ val table = ref cerror
+
+ local
+     val count = ref 0
+ in
+
+ fun resetKunif () = count := 0
+
+ fun kunif loc =
+     let
+         val n = !count
+         val s = if n <= 26 then
+                     str (chr (ord #"A" + n))
+                 else
+                     "U" ^ Int.toString (n - 26)
+     in
+         count := n + 1;
+         (L'.KUnif (loc, s, ref NONE), dummy)
+     end
+
+ end
+
+ local
+     val count = ref 0
+ in
+
+ fun resetCunif () = count := 0
+
+ fun cunif (loc, k) =
+     let
+         val n = !count
+         val s = if n <= 26 then
+                     str (chr (ord #"A" + n))
+                 else
+                     "U" ^ Int.toString (n - 26)
+     in
+         count := n + 1;
+         (L'.CUnif (loc, k, s, ref NONE), dummy)
+     end
+
+ end
+
+ fun elabKind (k, loc) =
+     case k of
+         L.KType => (L'.KType, loc)
+       | L.KArrow (k1, k2) => (L'.KArrow (elabKind k1, elabKind k2), loc)
+       | L.KName => (L'.KName, loc)
+       | L.KRecord k => (L'.KRecord (elabKind k), loc)
+       | L.KUnit => (L'.KUnit, loc)
+       | L.KTuple ks => (L'.KTuple (map elabKind ks), loc)
+       | L.KWild => kunif loc
+
+ fun foldKind (dom, ran, loc)=
+     (L'.KArrow ((L'.KArrow ((L'.KName, loc),
+                             (L'.KArrow (dom,
+                                         (L'.KArrow (ran, ran), loc)), loc)), loc),
+                 (L'.KArrow (ran,
+                             (L'.KArrow ((L'.KRecord dom, loc),
+                                         ran), loc)), loc)), loc)
+
+ fun hnormKind (kAll as (k, _)) =
+     case k of
+         L'.KUnif (_, _, ref (SOME k)) => hnormKind k
+       | _ => kAll
+
+ fun elabCon (env, denv) (c, loc) =
+     case c of
+         L.CAnnot (c, k) =>
+         let
+             val k' = elabKind k
+             val (c', ck, gs) = elabCon (env, denv) c
+         in
+             checkKind env c' ck k';
+             (c', k', gs)
+         end
+
+       | L.TFun (t1, t2) =>
+         let
+             val (t1', k1, gs1) = elabCon (env, denv) t1
+             val (t2', k2, gs2) = elabCon (env, denv) t2
+         in
+             checkKind env t1' k1 ktype;
+             checkKind env t2' k2 ktype;
+             ((L'.TFun (t1', t2'), loc), ktype, gs1 @ gs2)
+         end
+       | L.TCFun (e, x, k, t) =>
+         let
+             val e' = elabExplicitness e
+             val k' = elabKind k
+             val env' = E.pushCRel env x k'
+             val (t', tk, gs) = elabCon (env', D.enter denv) t
+         in
+             checkKind env t' tk ktype;
+             ((L'.TCFun (e', x, k', t'), loc), ktype, gs)
+         end
+       | L.CDisjoint (c1, c2, c) =>
+         let
+             val (c1', k1, gs1) = elabCon (env, denv) c1
+             val (c2', k2, gs2) = elabCon (env, denv) c2
+
+             val ku1 = kunif loc
+             val ku2 = kunif loc
+
+             val (denv', gs3) = D.assert env denv (c1', c2')
+             val (c', k, gs4) = elabCon (env, denv') c
+         in
+             checkKind env c1' k1 (L'.KRecord ku1, loc);
+             checkKind env c2' k2 (L'.KRecord ku2, loc);
+
+             ((L'.CDisjoint (L'.Instantiate, c1', c2', c'), loc), k, gs1 @ gs2 @ gs3 @ gs4)
+         end
+       | L.TRecord c =>
+         let
+             val (c', ck, gs) = elabCon (env, denv) c
+             val k = (L'.KRecord ktype, loc)
+         in
+             checkKind env c' ck k;
+             ((L'.TRecord c', loc), ktype, gs)
+         end
+
+       | L.CVar ([], s) =>
+         (case E.lookupC env s of
+              E.NotBound =>
+              (conError env (UnboundCon (loc, s));
+               (cerror, kerror, []))
+            | E.Rel (n, k) =>
+              ((L'.CRel n, loc), k, [])
+            | E.Named (n, k) =>
+              ((L'.CNamed n, loc), k, []))
+       | L.CVar (m1 :: ms, s) =>
+         (case E.lookupStr env m1 of
+              NONE => (conError env (UnboundStrInCon (loc, m1));
+                       (cerror, kerror, []))
+            | SOME (n, sgn) =>
+              let
+                  val (str, sgn) = foldl (fn (m, (str, sgn)) =>
+                                      case E.projectStr env {sgn = sgn, str = str, field = m} of
+                                          NONE => (conError env (UnboundStrInCon (loc, m));
+                                                   (strerror, sgnerror))
+                                        | SOME sgn => ((L'.StrProj (str, m), loc), sgn))
+                                   ((L'.StrVar n, loc), sgn) ms
+
+                  val k = case E.projectCon env {sgn = sgn, str = str, field = s} of
+                              NONE => (conError env (UnboundCon (loc, s));
+                                       kerror)
+                            | SOME (k, _) => k
+              in
+                  ((L'.CModProj (n, ms, s), loc), k, [])
+              end)
+
+       | L.CApp (c1, c2) =>
+         let
+             val (c1', k1, gs1) = elabCon (env, denv) c1
+             val (c2', k2, gs2) = elabCon (env, denv) c2
+             val dom = kunif loc
+             val ran = kunif loc
+         in
+             checkKind env c1' k1 (L'.KArrow (dom, ran), loc);
+             checkKind env c2' k2 dom;
+             ((L'.CApp (c1', c2'), loc), ran, gs1 @ gs2)
+         end
+       | L.CAbs (x, ko, t) =>
+         let
+             val k' = case ko of
+                          NONE => kunif loc
+                        | SOME k => elabKind k
+             val env' = E.pushCRel env x k'
+             val (t', tk, gs) = elabCon (env', D.enter denv) t
+         in
+             ((L'.CAbs (x, k', t'), loc),
+              (L'.KArrow (k', tk), loc),
+              gs)
+         end
+
+       | L.CName s =>
+         ((L'.CName s, loc), kname, [])
+
+       | L.CRecord xcs =>
+         let
+             val k = kunif loc
+
+             val (xcs', gs) = ListUtil.foldlMap (fn ((x, c), gs) =>
+                                                    let
+                                                        val (x', xk, gs1) = elabCon (env, denv) x
+                                                        val (c', ck, gs2) = elabCon (env, denv) c
+                                                    in
+                                                        checkKind env x' xk kname;
+                                                        checkKind env c' ck k;
+                                                        ((x', c'), gs1 @ gs2 @ gs)
+                                                    end) [] xcs
+
+             val rc = (L'.CRecord (k, xcs'), loc)
+             (* Add duplicate field checking later. *)
+
+             fun prove (xcs, ds) =
+                 case xcs of
+                     [] => ds
+                   | xc :: rest =>
+                     let
+                         val r1 = (L'.CRecord (k, [xc]), loc)
+                         val ds = foldl (fn (xc', ds) =>
+                                            let
+                                                val r2 = (L'.CRecord (k, [xc']), loc)
+                                            in
+                                                D.prove env denv (r1, r2, loc) @ ds
+                                            end)
+                                  ds rest
+                     in
+                         prove (rest, ds)
+                     end
+         in
+             (rc, (L'.KRecord k, loc), prove (xcs', gs))
+         end
+       | L.CConcat (c1, c2) =>
+         let
+             val (c1', k1, gs1) = elabCon (env, denv) c1
+             val (c2', k2, gs2) = elabCon (env, denv) c2
+             val ku = kunif loc
+             val k = (L'.KRecord ku, loc)
+         in
+             checkKind env c1' k1 k;
+             checkKind env c2' k2 k;
+             ((L'.CConcat (c1', c2'), loc), k,
+              D.prove env denv (c1', c2', loc) @ gs1 @ gs2)
+         end
+       | L.CFold =>
+         let
+             val dom = kunif loc
+             val ran = kunif loc
+         in
+             ((L'.CFold (dom, ran), loc),
+              foldKind (dom, ran, loc),
+              [])
+         end
+
+       | L.CUnit => ((L'.CUnit, loc), (L'.KUnit, loc), [])
+
+       | L.CTuple cs =>
+         let
+             val (cs', ks, gs) = foldl (fn (c, (cs', ks, gs)) =>
+                                           let
+                                               val (c', k, gs') = elabCon (env, denv) c
+                                           in
+                                               (c' :: cs', k :: ks, gs' @ gs)
+                                           end) ([], [], []) cs
+         in
+             ((L'.CTuple (rev cs'), loc), (L'.KTuple (rev ks), loc), gs)
+         end
+       | L.CProj (c, n) =>
+         let
+             val (c', k, gs) = elabCon (env, denv) c
+         in
+             case hnormKind k of
+                 (L'.KTuple ks, _) =>
+                 if n <= 0 orelse n > length ks then
+                     (conError env (ProjBounds (c', n));
                       (cerror, kerror, []))
-           | SOME (n, sgn) =>
+                 else
+                     ((L'.CProj (c', n), loc), List.nth (ks, n - 1), gs)
+               | k => (conError env (ProjMismatch (c', k));
+                       (cerror, kerror, []))
+         end
+
+       | L.CWild k =>
+         let
+             val k' = elabKind k
+         in
+             (cunif (loc, k'), k', [])
+         end
+
+ fun kunifsRemain k =
+     case k of
+         L'.KUnif (_, _, ref NONE) => true
+       | _ => false
+ fun cunifsRemain c =
+     case c of
+         L'.CUnif (loc, _, _, ref NONE) => SOME loc
+       | _ => NONE
+
+ val kunifsInDecl = U.Decl.exists {kind = kunifsRemain,
+                                   con = fn _ => false,
+                                   exp = fn _ => false,
+                                   sgn_item = fn _ => false,
+                                   sgn = fn _ => false,
+                                   str = fn _ => false,
+                                   decl = fn _ => false}
+
+ val cunifsInDecl = U.Decl.search {kind = fn _ => NONE,
+                                   con = cunifsRemain,
+                                   exp = fn _ => NONE,
+                                   sgn_item = fn _ => NONE,
+                                   sgn = fn _ => NONE,
+                                   str = fn _ => NONE,
+                                   decl = fn _ => NONE}
+
+ fun occursCon r =
+     U.Con.exists {kind = fn _ => false,
+                   con = fn L'.CUnif (_, _, _, r') => r = r'
+                          | _ => false}
+
+ exception CUnify' of cunify_error
+
+ exception SynUnif = E.SynUnif
+
+ open ElabOps
+
+ type record_summary = {
+      fields : (L'.con * L'.con) list,
+      unifs : (L'.con * L'.con option ref) list,
+      others : L'.con list
+ }
+
+ fun summaryToCon {fields, unifs, others} =
+     let
+         val c = (L'.CRecord (ktype, []), dummy)
+         val c = List.foldr (fn (c', c) => (L'.CConcat (c', c), dummy)) c others
+         val c = List.foldr (fn ((c', _), c) => (L'.CConcat (c', c), dummy)) c unifs
+     in
+         (L'.CConcat ((L'.CRecord (ktype, fields), dummy), c), dummy)
+     end
+
+ fun p_summary env s = p_con env (summaryToCon s)
+
+ exception CUnify of L'.con * L'.con * cunify_error
+
+ fun kindof env (c, loc) =
+     case c of
+         L'.TFun _ => ktype
+       | L'.TCFun _ => ktype
+       | L'.TRecord _ => ktype
+
+       | L'.CRel xn => #2 (E.lookupCRel env xn)
+       | L'.CNamed xn => #2 (E.lookupCNamed env xn)
+       | L'.CModProj (n, ms, x) =>
+         let
+             val (_, sgn) = E.lookupStrNamed env n
+             val (str, sgn) = foldl (fn (m, (str, sgn)) =>
+                                        case E.projectStr env {sgn = sgn, str = str, field = m} of
+                                            NONE => raise Fail "kindof: Unknown substructure"
+                                          | SOME sgn => ((L'.StrProj (str, m), loc), sgn))
+                              ((L'.StrVar n, loc), sgn) ms
+         in
+             case E.projectCon env {sgn = sgn, str = str, field = x} of
+                 NONE => raise Fail "kindof: Unknown con in structure"
+               | SOME (k, _) => k
+         end
+
+       | L'.CApp (c, _) =>
+         (case hnormKind (kindof env c) of
+              (L'.KArrow (_, k), _) => k
+            | (L'.KError, _) => kerror
+            | k => raise CUnify' (CKindof (k, c)))
+       | L'.CAbs (x, k, c) => (L'.KArrow (k, kindof (E.pushCRel env x k) c), loc)
+       | L'.CDisjoint (_, _, _, c) => kindof env c
+
+       | L'.CName _ => kname
+
+       | L'.CRecord (k, _) => (L'.KRecord k, loc)
+       | L'.CConcat (c, _) => kindof env c
+       | L'.CFold (dom, ran) => foldKind (dom, ran, loc)
+
+       | L'.CUnit => (L'.KUnit, loc)
+
+       | L'.CTuple cs => (L'.KTuple (map (kindof env) cs), loc)
+       | L'.CProj (c, n) =>
+         (case hnormKind (kindof env c) of
+              (L'.KTuple ks, _) => List.nth (ks, n - 1)
+            | k => raise CUnify' (CKindof (k, c)))
+
+       | L'.CError => kerror
+       | L'.CUnif (_, k, _, _) => k
+
+ val hnormCon = D.hnormCon
+
+ datatype con_summary =
+          Nil
+        | Cons
+        | Unknown
+
+ fun compatible cs =
+     case cs of
+         (Unknown, _) => false
+       | (_, Unknown) => false
+       | (s1, s2) => s1 = s2
+
+ fun summarizeCon (env, denv) c =
+     let
+         val (c, gs) = hnormCon (env, denv) c
+     in
+         case #1 c of
+             L'.CRecord (_, []) => (Nil, gs)
+           | L'.CRecord (_, _ :: _) => (Cons, gs)
+           | L'.CConcat ((L'.CRecord (_, _ :: _), _), _) => (Cons, gs)
+           | L'.CDisjoint (_, _, _, c) =>
              let
-                 val (str, sgn) = foldl (fn (m, (str, sgn)) =>
-                                     case E.projectStr env {sgn = sgn, str = str, field = m} of
-                                         NONE => (conError env (UnboundStrInCon (loc, m));
-                                                  (strerror, sgnerror))
-                                       | SOME sgn => ((L'.StrProj (str, m), loc), sgn))
-                                  ((L'.StrVar n, loc), sgn) ms
-
-                 val k = case E.projectCon env {sgn = sgn, str = str, field = s} of
-                             NONE => (conError env (UnboundCon (loc, s));
-                                      kerror)
-                           | SOME (k, _) => k
+                 val (s, gs') = summarizeCon (env, denv) c
              in
-                 ((L'.CModProj (n, ms, s), loc), k, [])
-             end)
-                                                                       
-      | L.CApp (c1, c2) =>
-        let
-            val (c1', k1, gs1) = elabCon (env, denv) c1
-            val (c2', k2, gs2) = elabCon (env, denv) c2
-            val dom = kunif loc
-            val ran = kunif loc
-        in
-            checkKind env c1' k1 (L'.KArrow (dom, ran), loc);
-            checkKind env c2' k2 dom;
-            ((L'.CApp (c1', c2'), loc), ran, gs1 @ gs2)
-        end
-      | L.CAbs (x, ko, t) =>
-        let
-            val k' = case ko of
-                         NONE => kunif loc
-                       | SOME k => elabKind k
-            val env' = E.pushCRel env x k'
-            val (t', tk, gs) = elabCon (env', D.enter denv) t
-        in
-            ((L'.CAbs (x, k', t'), loc),
-             (L'.KArrow (k', tk), loc),
-             gs)
-        end
-
-      | L.CName s =>
-        ((L'.CName s, loc), kname, [])
-
-      | L.CRecord xcs =>
-        let
-            val k = kunif loc
-
-            val (xcs', gs) = ListUtil.foldlMap (fn ((x, c), gs) =>
-                                                   let
-                                                       val (x', xk, gs1) = elabCon (env, denv) x
-                                                       val (c', ck, gs2) = elabCon (env, denv) c
-                                                   in
-                                                       checkKind env x' xk kname;
-                                                       checkKind env c' ck k;
-                                                       ((x', c'), gs1 @ gs2 @ gs)
-                                                   end) [] xcs
-
-            val rc = (L'.CRecord (k, xcs'), loc)
-            (* Add duplicate field checking later. *)
-
-            fun prove (xcs, ds) =
-                case xcs of
-                    [] => ds
-                  | xc :: rest =>
-                    let
-                        val r1 = (L'.CRecord (k, [xc]), loc)
-                        val ds = foldl (fn (xc', ds) =>
-                                           let
-                                               val r2 = (L'.CRecord (k, [xc']), loc)
-                                           in
-                                               D.prove env denv (r1, r2, loc) @ ds
-                                           end)
-                                 ds rest
-                    in
-                        prove (rest, ds)
-                    end
-        in
-            (rc, (L'.KRecord k, loc), prove (xcs', gs))
-        end
-      | L.CConcat (c1, c2) =>
-        let
-            val (c1', k1, gs1) = elabCon (env, denv) c1
-            val (c2', k2, gs2) = elabCon (env, denv) c2
-            val ku = kunif loc
-            val k = (L'.KRecord ku, loc)
-        in
-            checkKind env c1' k1 k;
-            checkKind env c2' k2 k;
-            ((L'.CConcat (c1', c2'), loc), k,
-             D.prove env denv (c1', c2', loc) @ gs1 @ gs2)
-        end
-      | L.CFold =>
-        let
-            val dom = kunif loc
-            val ran = kunif loc
-        in
-            ((L'.CFold (dom, ran), loc),
-             foldKind (dom, ran, loc),
+                 (s, gs @ gs')
+             end
+           | _ => (Unknown, gs)
+     end
+
+ fun p_con_summary s =
+     Print.PD.string (case s of
+                          Nil => "Nil"
+                        | Cons => "Cons"
+                        | Unknown => "Unknown")
+
+ exception SummaryFailure
+
+ fun unifyRecordCons (env, denv) (c1, c2) =
+     let
+         fun rkindof c =
+             case hnormKind (kindof env c) of
+                 (L'.KRecord k, _) => k
+               | (L'.KError, _) => kerror
+               | k => raise CUnify' (CKindof (k, c))
+
+         val k1 = rkindof c1
+         val k2 = rkindof c2
+
+         val (r1, gs1) = recordSummary (env, denv) c1
+         val (r2, gs2) = recordSummary (env, denv) c2
+     in
+         unifyKinds k1 k2;
+         unifySummaries (env, denv) (k1, r1, r2);
+         gs1 @ gs2
+     end
+
+ and recordSummary (env, denv) c =
+     let
+         val (c, gs) = hnormCon (env, denv) c
+
+         val (sum, gs') =
+             case c of
+                 (L'.CRecord (_, xcs), _) => ({fields = xcs, unifs = [], others = []}, [])
+               | (L'.CConcat (c1, c2), _) =>
+                 let
+                     val (s1, gs1) = recordSummary (env, denv) c1
+                     val (s2, gs2) = recordSummary (env, denv) c2
+                 in
+                     ({fields = #fields s1 @ #fields s2,
+                       unifs = #unifs s1 @ #unifs s2,
+                       others = #others s1 @ #others s2},
+                      gs1 @ gs2)
+                 end
+               | (L'.CUnif (_, _, _, ref (SOME c)), _) => recordSummary (env, denv) c
+               | c' as (L'.CUnif (_, _, _, r), _) => ({fields = [], unifs = [(c', r)], others = []}, [])
+               | c' => ({fields = [], unifs = [], others = [c']}, [])
+     in
+         (sum, gs @ gs')
+     end
+
+ and consEq (env, denv) (c1, c2) =
+     let
+         val gs = unifyCons (env, denv) c1 c2
+     in
+         List.all (fn (loc, env, denv, c1, c2) =>
+                      case D.prove env denv (c1, c2, loc) of
+                          [] => true
+                        | _ => false) gs
+     end
+     handle CUnify _ => false
+
+ and consNeq env (c1, c2) =
+     case (#1 (ElabOps.hnormCon env c1), #1 (ElabOps.hnormCon env c2)) of
+         (L'.CName x1, L'.CName x2) => x1 <> x2
+       | _ => false
+
+ and unifySummaries (env, denv) (k, s1 : record_summary, s2 : record_summary) =
+     let
+         val loc = #2 k
+         (*val () = eprefaces "Summaries" [("#1", p_summary env s1),
+                                         ("#2", p_summary env s2)]*)
+
+         fun eatMatching p (ls1, ls2) =
+             let
+                 fun em (ls1, ls2, passed1) =
+                     case ls1 of
+                         [] => (rev passed1, ls2)
+                       | h1 :: t1 =>
+                         let
+                             fun search (ls2', passed2) =
+                                 case ls2' of
+                                     [] => em (t1, ls2, h1 :: passed1)
+                                   | h2 :: t2 =>
+                                     if p (h1, h2) then
+                                         em (t1, List.revAppend (passed2, t2), passed1)
+                                     else
+                                         search (t2, h2 :: passed2)
+                         in
+                             search (ls2, [])
+                         end
+             in
+                 em (ls1, ls2, [])
+             end
+
+         val (fs1, fs2) = eatMatching (fn ((x1, c1), (x2, c2)) =>
+                                          not (consNeq env (x1, x2))
+                                          andalso consEq (env, denv) (c1, c2)
+                                          andalso consEq (env, denv) (x1, x2))
+                                      (#fields s1, #fields s2)
+         (*val () = eprefaces "Summaries2" [("#1", p_summary env {fields = fs1, unifs = #unifs s1, others = #others s1}),
+                                          ("#2", p_summary env {fields = fs2, unifs = #unifs s2, others = #others s2})]*)
+         val (unifs1, unifs2) = eatMatching (fn ((_, r1), (_, r2)) => r1 = r2) (#unifs s1, #unifs s2)
+         val (others1, others2) = eatMatching (consEq (env, denv)) (#others s1, #others s2)
+         (*val () = eprefaces "Summaries3" [("#1", p_summary env {fields = fs1, unifs = unifs1, others = others1}),
+                                          ("#2", p_summary env {fields = fs2, unifs = unifs2, others = others2})]*)
+
+         fun unifFields (fs, others, unifs) =
+             case (fs, others, unifs) of
+                 ([], [], _) => ([], [], unifs)
+               | (_, _, []) => (fs, others, [])
+               | (_, _, (_, r) :: rest) =>
+                 let
+                     val r' = ref NONE
+                     val kr = (L'.KRecord k, dummy)
+                     val cr' = (L'.CUnif (dummy, kr, "recd", r'), dummy)
+
+                     val prefix = case (fs, others) of
+                                      ([], other :: others) =>
+                                      List.foldl (fn (other, c) =>
+                                                     (L'.CConcat (c, other), dummy))
+                                                 other others
+                                    | (fs, []) =>
+                                      (L'.CRecord (k, fs), dummy)
+                                    | (fs, others) =>
+                                      List.foldl (fn (other, c) =>
+                                                     (L'.CConcat (c, other), dummy))
+                                                 (L'.CRecord (k, fs), dummy) others
+                 in
+                     r := SOME (L'.CConcat (prefix, cr'), dummy);
+                     ([], [], (cr', r') :: rest)
+                 end
+
+         val (fs1, others1, unifs2) = unifFields (fs1, others1, unifs2)
+         val (fs2, others2, unifs1) = unifFields (fs2, others2, unifs1)
+
+         (*val () = eprefaces "Summaries4" [("#1", p_summary env {fields = fs1, unifs = unifs1, others = others1}),
+                                          ("#2", p_summary env {fields = fs2, unifs = unifs2, others = others2})]*)
+
+         fun isGuessable (other, fs) =
+             let
+                 val gs = guessFold (env, denv) (other, (L'.CRecord (k, fs), loc), [], SummaryFailure)
+             in
+                 List.all (fn (loc, env, denv, c1, c2) =>
+                              case D.prove env denv (c1, c2, loc) of
+                                  [] => true
+                                | _ => false) gs
+             end
+             handle SummaryFailure => false
+
+         val (fs1, fs2, others1, others2) =
+             case (fs1, fs2, others1, others2) of
+                 ([], _, [other1], []) =>
+                 if isGuessable (other1, fs2) then
+                     ([], [], [], [])
+                 else
+                     (fs1, fs2, others1, others2)
+               | (_, [], [], [other2]) =>
+                 if isGuessable (other2, fs1) then
+                     ([], [], [], [])
+                 else
+                     (fs1, fs2, others1, others2)
+               | _ => (fs1, fs2, others1, others2)
+
+         (*val () = eprefaces "Summaries5" [("#1", p_summary env {fields = fs1, unifs = unifs1, others = others1}),
+                                            ("#2", p_summary env {fields = fs2, unifs = unifs2, others = others2})]*)
+
+         val clear = case (fs1, others1, fs2, others2) of
+                          ([], [], [], []) => true
+                        | _ => false
+         val empty = (L'.CRecord (k, []), dummy)
+
+         fun unsummarize {fields, unifs, others} =
+             let
+                 val c = (L'.CRecord (k, fields), loc)
+
+                 val c = foldl (fn ((c1, _), c2) => (L'.CConcat (c1, c2), loc))
+                               c unifs
+             in
+                 foldl (fn (c1, c2) => (L'.CConcat (c1, c2), loc))
+                       c others
+             end
+
+         fun pairOffUnifs (unifs1, unifs2) =
+             case (unifs1, unifs2) of
+                 ([], _) =>
+                 if clear then
+                     List.app (fn (_, r) => r := SOME empty) unifs2
+                 else
+                     raise CUnify' (CRecordFailure (unsummarize s1, unsummarize s2))
+               | (_, []) =>
+                 if clear then
+                     List.app (fn (_, r) => r := SOME empty) unifs1
+                 else
+                     raise CUnify' (CRecordFailure (unsummarize s1, unsummarize s2))
+               | ((c1, _) :: rest1, (_, r2) :: rest2) =>
+                 (r2 := SOME c1;
+                  pairOffUnifs (rest1, rest2))
+     in
+         pairOffUnifs (unifs1, unifs2)
+         (*before eprefaces "Summaries'" [("#1", p_summary env s1),
+                                       ("#2", p_summary env s2)]*)
+     end
+
+ and guessFold (env, denv) (c1, c2, gs, ex) =
+     let
+         val loc = #2 c1
+
+         fun unfold (dom, ran, f, i, r, c) =
+             let
+                 val nm = cunif (loc, (L'.KName, loc))
+                 val v = cunif (loc, dom)
+                 val rest = cunif (loc, (L'.KRecord dom, loc))
+                 val acc = (L'.CFold (dom, ran), loc)
+                 val acc = (L'.CApp (acc, f), loc)
+                 val acc = (L'.CApp (acc, i), loc)
+                 val acc = (L'.CApp (acc, rest), loc)
+
+                 val (iS, gs3) = summarizeCon (env, denv) i
+
+                 val app = (L'.CApp (f, nm), loc)
+                 val app = (L'.CApp (app, v), loc)
+                 val app = (L'.CApp (app, acc), loc)
+                 val (appS, gs4) = summarizeCon (env, denv) app
+
+                 val (cS, gs5) = summarizeCon (env, denv) c
+             in
+                 (*prefaces "Summaries" [("iS", p_con_summary iS),
+                                         ("appS", p_con_summary appS),
+                                         ("cS", p_con_summary cS)];*)
+
+                 if compatible (iS, appS) then
+                     raise ex
+                 else if compatible (cS, iS) then
+                     let
+                         (*val () = prefaces "Same?" [("i", p_con env i),
+                                                    ("c", p_con env c)]*)
+                         val gs6 = unifyCons (env, denv) i c
+                         (*val () = TextIO.print "Yes!\n"*)
+
+                         val gs7 = unifyCons (env, denv) r (L'.CRecord (dom, []), loc)
+                     in
+                         gs @ gs3 @ gs5 @ gs6 @ gs7
+                     end
+                 else if compatible (cS, appS) then
+                     let
+                         (*val () = prefaces "Same?" [("app", p_con env app),
+                                                      ("c", p_con env c),
+                                                      ("app'", p_con env (#1 (hnormCon (env, denv) app)))]*)
+                         val gs6 = unifyCons (env, denv) app c
+                         (*val () = TextIO.print "Yes!\n"*)
+
+                         val singleton = (L'.CRecord (dom, [(nm, v)]), loc)
+                         val concat = (L'.CConcat (singleton, rest), loc)
+                         (*val () = prefaces "Pre-crew" [("r", p_con env r),
+                                                         ("concat", p_con env concat)]*)
+                         val gs7 = unifyCons (env, denv) r concat
+                     in
+                         (*prefaces "The crew" [("nm", p_con env nm),
+                                                ("v", p_con env v),
+                                                ("rest", p_con env rest)];*)
+
+                         gs @ gs3 @ gs4 @ gs5 @ gs6 @ gs7
+                     end
+                 else
+                     raise ex
+             end
+             handle _ => raise ex
+     in
+         case (#1 c1, #1 c2) of
+             (L'.CApp ((L'.CApp ((L'.CApp ((L'.CFold (dom, ran), _), f), _), i), _), r), _) =>
+             unfold (dom, ran, f, i, r, c2)
+           | (_, L'.CApp ((L'.CApp ((L'.CApp ((L'.CFold (dom, ran), _), f), _), i), _), r)) =>
+             unfold (dom, ran, f, i, r, c1)
+           | _ => raise ex
+     end
+
+ and unifyCons' (env, denv) c1 c2 =
+     case (#1 c1, #1 c2) of
+         (L'.CDisjoint (_, x1, y1, t1), L'.CDisjoint (_, x2, y2, t2)) =>
+         let
+             val gs1 = unifyCons' (env, denv) x1 x2
+             val gs2 = unifyCons' (env, denv) y1 y2
+             val (denv', gs3) = D.assert env denv (x1, y1)
+             val gs4 = unifyCons' (env, denv') t1 t2
+         in
+             gs1 @ gs2 @ gs3 @ gs4
+         end
+       | _ =>
+         let
+             val (c1, gs1) = hnormCon (env, denv) c1
+             val (c2, gs2) = hnormCon (env, denv) c2
+         in
+             let
+                 val gs3 = unifyCons'' (env, denv) c1 c2
+             in
+                 gs1 @ gs2 @ gs3
+             end
+             handle ex => guessFold (env, denv) (c1, c2, gs1 @ gs2, ex)
+         end
+
+ and unifyCons'' (env, denv) (c1All as (c1, loc)) (c2All as (c2, _)) =
+     let
+         fun err f = raise CUnify' (f (c1All, c2All))
+
+         fun isRecord () = unifyRecordCons (env, denv) (c1All, c2All)
+     in
+         (*eprefaces "unifyCons''" [("c1All", p_con env c1All),
+                                  ("c2All", p_con env c2All)];*)
+
+         case (c1, c2) of
+             (L'.CUnit, L'.CUnit) => []
+
+           | (L'.TFun (d1, r1), L'.TFun (d2, r2)) =>
+             unifyCons' (env, denv) d1 d2
+             @ unifyCons' (env, denv) r1 r2
+           | (L'.TCFun (expl1, x1, d1, r1), L'.TCFun (expl2, _, d2, r2)) =>
+             if expl1 <> expl2 then
+                 err CExplicitness
+             else
+                 (unifyKinds d1 d2;
+                  unifyCons' (E.pushCRel env x1 d1, D.enter denv) r1 r2)
+           | (L'.TRecord r1, L'.TRecord r2) => unifyCons' (env, denv) r1 r2
+
+           | (L'.CRel n1, L'.CRel n2) =>
+             if n1 = n2 then
+                 []
+             else
+                 err CIncompatible
+           | (L'.CNamed n1, L'.CNamed n2) =>
+             if n1 = n2 then
+                 []
+             else
+                 err CIncompatible
+
+           | (L'.CApp (d1, r1), L'.CApp (d2, r2)) =>
+             (unifyCons' (env, denv) d1 d2;
+              unifyCons' (env, denv) r1 r2)
+           | (L'.CAbs (x1, k1, c1), L'.CAbs (_, k2, c2)) =>
+             (unifyKinds k1 k2;
+              unifyCons' (E.pushCRel env x1 k1, D.enter denv) c1 c2)
+
+           | (L'.CName n1, L'.CName n2) =>
+             if n1 = n2 then
+                 []
+             else
+                 err CIncompatible
+
+           | (L'.CModProj (n1, ms1, x1), L'.CModProj (n2, ms2, x2)) =>
+             if n1 = n2 andalso ms1 = ms2 andalso x1 = x2 then
+                 []
+             else
+                 err CIncompatible
+
+           | (L'.CTuple cs1, L'.CTuple cs2) =>
+             ((ListPair.foldlEq (fn (c1, c2, gs) =>
+                                    let
+                                        val gs' = unifyCons' (env, denv) c1 c2
+                                    in
+                                        gs' @ gs
+                                    end) [] (cs1, cs2))
+              handle ListPair.UnequalLengths => err CIncompatible)
+
+           | (L'.CProj ((L'.CUnif (_, _, _, ref (SOME c1)), loc), n1), _) =>
+             unifyCons' (env, denv) (L'.CProj (c1, n1), loc) c2All
+           | (_, L'.CProj ((L'.CUnif (_, _, _, ref (SOME c2)), loc), n2)) =>
+             unifyCons' (env, denv) c1All (L'.CProj (c2, n2), loc)
+           | (L'.CProj ((L'.CUnif (_, (L'.KTuple ks, _), _, r), loc), n), _) =>
+             let
+                 val us = map (fn k => cunif (loc, k)) ks
+             in
+                 r := SOME (L'.CTuple us, loc);
+                 unifyCons' (env, denv) (List.nth (us, n - 1)) c2All
+             end
+           | (_, L'.CProj ((L'.CUnif (_, (L'.KTuple ks, _), _, r), loc), n)) =>
+             let
+                 val us = map (fn k => cunif (loc, k)) ks
+             in
+                 r := SOME (L'.CTuple us, loc);
+                 unifyCons' (env, denv) c1All (List.nth (us, n - 1))
+             end
+           | (L'.CProj (c1, n1), L'.CProj (c2, n2)) =>
+             if n1 = n2 then
+                 unifyCons' (env, denv) c1 c2
+             else
+                 err CIncompatible
+
+           | (L'.CError, _) => []
+           | (_, L'.CError) => []
+
+           | (L'.CRecord _, _) => isRecord ()
+           | (_, L'.CRecord _) => isRecord ()
+           | (L'.CConcat _, _) => isRecord ()
+           | (_, L'.CConcat _) => isRecord ()
+
+           | (L'.CUnif (_, k1, _, r1), L'.CUnif (_, k2, _, r2)) =>
+             if r1 = r2 then
+                 []
+             else
+                 (unifyKinds k1 k2;
+                  r1 := SOME c2All;
+                  [])
+
+           | (L'.CUnif (_, _, _, r), _) =>
+             if occursCon r c2All then
+                 err COccursCheckFailed
+             else
+                 (r := SOME c2All;
+                  [])
+           | (_, L'.CUnif (_, _, _, r)) =>
+             if occursCon r c1All then
+                 err COccursCheckFailed
+             else
+                 (r := SOME c1All;
+                  [])
+
+           | (L'.CFold (dom1, ran1), L'.CFold (dom2, ran2)) =>
+             (unifyKinds dom1 dom2;
+              unifyKinds ran1 ran2;
+              [])
+
+           | _ => err CIncompatible
+     end
+
+ and unifyCons (env, denv) c1 c2 =
+     unifyCons' (env, denv) c1 c2
+     handle CUnify' err => raise CUnify (c1, c2, err)
+          | KUnify args => raise CUnify (c1, c2, CKind args)
+
+ fun checkCon (env, denv) e c1 c2 =
+     unifyCons (env, denv) c1 c2
+     handle CUnify (c1, c2, err) =>
+            (expError env (Unify (e, c1, c2, err));
              [])
-        end
-
-      | L.CUnit => ((L'.CUnit, loc), (L'.KUnit, loc), [])
-
-      | L.CTuple cs =>
-        let
-            val (cs', ks, gs) = foldl (fn (c, (cs', ks, gs)) =>
-                                          let
-                                              val (c', k, gs') = elabCon (env, denv) c
-                                          in
-                                              (c' :: cs', k :: ks, gs' @ gs)
-                                          end) ([], [], []) cs
-        in
-            ((L'.CTuple (rev cs'), loc), (L'.KTuple (rev ks), loc), gs)
-        end
-      | L.CProj (c, n) =>
-        let
-            val (c', k, gs) = elabCon (env, denv) c
-        in
-            case hnormKind k of
-                (L'.KTuple ks, _) =>
-                if n <= 0 orelse n > length ks then
-                    (conError env (ProjBounds (c', n));
-                     (cerror, kerror, []))
-                else
-                    ((L'.CProj (c', n), loc), List.nth (ks, n - 1), gs)
-              | k => (conError env (ProjMismatch (c', k));
-                      (cerror, kerror, []))
-        end
-
-      | L.CWild k =>
-        let
-            val k' = elabKind k
-        in
-            (cunif (loc, k'), k', [])
-        end
-
-fun kunifsRemain k =
-    case k of
-        L'.KUnif (_, _, ref NONE) => true
-      | _ => false
-fun cunifsRemain c =
-    case c of
-        L'.CUnif (loc, _, _, ref NONE) => SOME loc
-      | _ => NONE
-
-val kunifsInDecl = U.Decl.exists {kind = kunifsRemain,
-                                  con = fn _ => false,
-                                  exp = fn _ => false,
-                                  sgn_item = fn _ => false,
-                                  sgn = fn _ => false,
-                                  str = fn _ => false,
-                                  decl = fn _ => false}
-
-val cunifsInDecl = U.Decl.search {kind = fn _ => NONE,
-                                  con = cunifsRemain,
-                                  exp = fn _ => NONE,
-                                  sgn_item = fn _ => NONE,
-                                  sgn = fn _ => NONE,
-                                  str = fn _ => NONE,
-                                  decl = fn _ => NONE}
-
-fun occursCon r =
-    U.Con.exists {kind = fn _ => false,
-                  con = fn L'.CUnif (_, _, _, r') => r = r'
-                         | _ => false}
-
-exception CUnify' of cunify_error
-
-exception SynUnif = E.SynUnif
-
-open ElabOps
-
-type record_summary = {
-     fields : (L'.con * L'.con) list,
-     unifs : (L'.con * L'.con option ref) list,
-     others : L'.con list
-}
-
-fun summaryToCon {fields, unifs, others} =
-    let
-        val c = (L'.CRecord (ktype, []), dummy)
-        val c = List.foldr (fn (c', c) => (L'.CConcat (c', c), dummy)) c others
-        val c = List.foldr (fn ((c', _), c) => (L'.CConcat (c', c), dummy)) c unifs
-    in
-        (L'.CConcat ((L'.CRecord (ktype, fields), dummy), c), dummy)
-    end
-
-fun p_summary env s = p_con env (summaryToCon s)
-
-exception CUnify of L'.con * L'.con * cunify_error
-
-fun kindof env (c, loc) =
-    case c of
-        L'.TFun _ => ktype
-      | L'.TCFun _ => ktype
-      | L'.TRecord _ => ktype
-
-      | L'.CRel xn => #2 (E.lookupCRel env xn)
-      | L'.CNamed xn => #2 (E.lookupCNamed env xn)
-      | L'.CModProj (n, ms, x) =>
-        let
-            val (_, sgn) = E.lookupStrNamed env n
-            val (str, sgn) = foldl (fn (m, (str, sgn)) =>
-                                       case E.projectStr env {sgn = sgn, str = str, field = m} of
-                                           NONE => raise Fail "kindof: Unknown substructure"
-                                         | SOME sgn => ((L'.StrProj (str, m), loc), sgn))
-                             ((L'.StrVar n, loc), sgn) ms
-        in
-            case E.projectCon env {sgn = sgn, str = str, field = x} of
-                NONE => raise Fail "kindof: Unknown con in structure"
-              | SOME (k, _) => k
-        end
-
-      | L'.CApp (c, _) =>
-        (case hnormKind (kindof env c) of
-             (L'.KArrow (_, k), _) => k
-           | (L'.KError, _) => kerror
-           | k => raise CUnify' (CKindof (k, c)))
-      | L'.CAbs (x, k, c) => (L'.KArrow (k, kindof (E.pushCRel env x k) c), loc)
-      | L'.CDisjoint (_, _, _, c) => kindof env c
-
-      | L'.CName _ => kname
-
-      | L'.CRecord (k, _) => (L'.KRecord k, loc)
-      | L'.CConcat (c, _) => kindof env c
-      | L'.CFold (dom, ran) => foldKind (dom, ran, loc)
-
-      | L'.CUnit => (L'.KUnit, loc)
-
-      | L'.CTuple cs => (L'.KTuple (map (kindof env) cs), loc)
-      | L'.CProj (c, n) =>
-        (case hnormKind (kindof env c) of
-             (L'.KTuple ks, _) => List.nth (ks, n - 1)
-           | k => raise CUnify' (CKindof (k, c)))
-
-      | L'.CError => kerror
-      | L'.CUnif (_, k, _, _) => k
-
-val hnormCon = D.hnormCon
-
-datatype con_summary =
-         Nil
-       | Cons
-       | Unknown
-
-fun compatible cs =
-    case cs of
-        (Unknown, _) => false
-      | (_, Unknown) => false
-      | (s1, s2) => s1 = s2
-
-fun summarizeCon (env, denv) c =
-    let
-        val (c, gs) = hnormCon (env, denv) c
-    in
-        case #1 c of
-            L'.CRecord (_, []) => (Nil, gs)
-          | L'.CRecord (_, _ :: _) => (Cons, gs)
-          | L'.CConcat ((L'.CRecord (_, _ :: _), _), _) => (Cons, gs)
-          | L'.CDisjoint (_, _, _, c) =>
-            let
-                val (s, gs') = summarizeCon (env, denv) c
-            in
-                (s, gs @ gs')
-            end
-          | _ => (Unknown, gs)
-    end
-
-fun p_con_summary s =
-    Print.PD.string (case s of
-                         Nil => "Nil"
-                       | Cons => "Cons"
-                       | Unknown => "Unknown")
-
-exception SummaryFailure
-
-fun unifyRecordCons (env, denv) (c1, c2) =
-    let
-        fun rkindof c =
-            case hnormKind (kindof env c) of
-                (L'.KRecord k, _) => k
-              | (L'.KError, _) => kerror
-              | k => raise CUnify' (CKindof (k, c))
-
-        val k1 = rkindof c1
-        val k2 = rkindof c2
-
-        val (r1, gs1) = recordSummary (env, denv) c1
-        val (r2, gs2) = recordSummary (env, denv) c2
-    in
-        unifyKinds k1 k2;
-        unifySummaries (env, denv) (k1, r1, r2);
-        gs1 @ gs2
-    end
-
-and recordSummary (env, denv) c =
-    let
-        val (c, gs) = hnormCon (env, denv) c
-
-        val (sum, gs') =
-            case c of
-                (L'.CRecord (_, xcs), _) => ({fields = xcs, unifs = [], others = []}, [])
-              | (L'.CConcat (c1, c2), _) =>
-                let
-                    val (s1, gs1) = recordSummary (env, denv) c1
-                    val (s2, gs2) = recordSummary (env, denv) c2
-                in
-                    ({fields = #fields s1 @ #fields s2,
-                      unifs = #unifs s1 @ #unifs s2,
-                      others = #others s1 @ #others s2},
-                     gs1 @ gs2)
-                end
-              | (L'.CUnif (_, _, _, ref (SOME c)), _) => recordSummary (env, denv) c
-              | c' as (L'.CUnif (_, _, _, r), _) => ({fields = [], unifs = [(c', r)], others = []}, [])
-              | c' => ({fields = [], unifs = [], others = [c']}, [])
-    in
-        (sum, gs @ gs')
-    end
-
-and consEq (env, denv) (c1, c2) =
-    let
-        val gs = unifyCons (env, denv) c1 c2
-    in
-        List.all (fn (loc, env, denv, c1, c2) =>
-                     case D.prove env denv (c1, c2, loc) of
-                         [] => true
-                       | _ => false) gs
-    end
-    handle CUnify _ => false
-
-and consNeq env (c1, c2) =
-    case (#1 (ElabOps.hnormCon env c1), #1 (ElabOps.hnormCon env c2)) of
-        (L'.CName x1, L'.CName x2) => x1 <> x2
-      | _ => false
-
-and unifySummaries (env, denv) (k, s1 : record_summary, s2 : record_summary) =
-    let
-        val loc = #2 k
-        (*val () = eprefaces "Summaries" [("#1", p_summary env s1),
-                                        ("#2", p_summary env s2)]*)
-
-        fun eatMatching p (ls1, ls2) =
-            let
-                fun em (ls1, ls2, passed1) =
-                    case ls1 of
-                        [] => (rev passed1, ls2)
-                      | h1 :: t1 =>
-                        let
-                            fun search (ls2', passed2) =
-                                case ls2' of
-                                    [] => em (t1, ls2, h1 :: passed1)
-                                  | h2 :: t2 =>
-                                    if p (h1, h2) then
-                                        em (t1, List.revAppend (passed2, t2), passed1)
-                                    else
-                                        search (t2, h2 :: passed2)
-                        in
-                            search (ls2, [])
-                        end
-            in
-                em (ls1, ls2, [])
-            end
-
-        val (fs1, fs2) = eatMatching (fn ((x1, c1), (x2, c2)) =>
-                                         not (consNeq env (x1, x2))
-                                         andalso consEq (env, denv) (c1, c2)
-                                         andalso consEq (env, denv) (x1, x2))
-                                     (#fields s1, #fields s2)
-        (*val () = eprefaces "Summaries2" [("#1", p_summary env {fields = fs1, unifs = #unifs s1, others = #others s1}),
-                                         ("#2", p_summary env {fields = fs2, unifs = #unifs s2, others = #others s2})]*)
-        val (unifs1, unifs2) = eatMatching (fn ((_, r1), (_, r2)) => r1 = r2) (#unifs s1, #unifs s2)
-        val (others1, others2) = eatMatching (consEq (env, denv)) (#others s1, #others s2)
-        (*val () = eprefaces "Summaries3" [("#1", p_summary env {fields = fs1, unifs = unifs1, others = others1}),
-                                         ("#2", p_summary env {fields = fs2, unifs = unifs2, others = others2})]*)
-
-        fun unifFields (fs, others, unifs) =
-            case (fs, others, unifs) of
-                ([], [], _) => ([], [], unifs)
-              | (_, _, []) => (fs, others, [])
-              | (_, _, (_, r) :: rest) =>
-                let
-                    val r' = ref NONE
-                    val kr = (L'.KRecord k, dummy)
-                    val cr' = (L'.CUnif (dummy, kr, "recd", r'), dummy)
-
-                    val prefix = case (fs, others) of
-                                     ([], other :: others) =>
-                                     List.foldl (fn (other, c) =>
-                                                    (L'.CConcat (c, other), dummy))
-                                                other others
-                                   | (fs, []) =>
-                                     (L'.CRecord (k, fs), dummy)
-                                   | (fs, others) =>
-                                     List.foldl (fn (other, c) =>
-                                                    (L'.CConcat (c, other), dummy))
-                                                (L'.CRecord (k, fs), dummy) others
-                in
-                    r := SOME (L'.CConcat (prefix, cr'), dummy);
-                    ([], [], (cr', r') :: rest)
-                end
-
-        val (fs1, others1, unifs2) = unifFields (fs1, others1, unifs2)
-        val (fs2, others2, unifs1) = unifFields (fs2, others2, unifs1)
-
-        (*val () = eprefaces "Summaries4" [("#1", p_summary env {fields = fs1, unifs = unifs1, others = others1}),
-                                         ("#2", p_summary env {fields = fs2, unifs = unifs2, others = others2})]*)
-
-        fun isGuessable (other, fs) =
-            let
-                val gs = guessFold (env, denv) (other, (L'.CRecord (k, fs), loc), [], SummaryFailure)
-            in
-                List.all (fn (loc, env, denv, c1, c2) =>
-                             case D.prove env denv (c1, c2, loc) of
-                                 [] => true
-                               | _ => false) gs
-            end
-            handle SummaryFailure => false
-
-        val (fs1, fs2, others1, others2) =
-            case (fs1, fs2, others1, others2) of
-                ([], _, [other1], []) =>
-                if isGuessable (other1, fs2) then
-                    ([], [], [], [])
-                else
-                    (fs1, fs2, others1, others2)
-              | (_, [], [], [other2]) =>
-                if isGuessable (other2, fs1) then
-                    ([], [], [], [])
-                else
-                    (fs1, fs2, others1, others2)
-              | _ => (fs1, fs2, others1, others2)
-
-        (*val () = eprefaces "Summaries5" [("#1", p_summary env {fields = fs1, unifs = unifs1, others = others1}),
-                                           ("#2", p_summary env {fields = fs2, unifs = unifs2, others = others2})]*)
-
-        val clear = case (fs1, others1, fs2, others2) of
-                         ([], [], [], []) => true
-                       | _ => false
-        val empty = (L'.CRecord (k, []), dummy)
-
-        fun unsummarize {fields, unifs, others} =
-            let
-                val c = (L'.CRecord (k, fields), loc)
-
-                val c = foldl (fn ((c1, _), c2) => (L'.CConcat (c1, c2), loc))
-                              c unifs
-            in
-                foldl (fn (c1, c2) => (L'.CConcat (c1, c2), loc))
-                      c others
-            end
-
-        fun pairOffUnifs (unifs1, unifs2) =
-            case (unifs1, unifs2) of
-                ([], _) =>
-                if clear then
-                    List.app (fn (_, r) => r := SOME empty) unifs2
-                else
-                    raise CUnify' (CRecordFailure (unsummarize s1, unsummarize s2))
-              | (_, []) =>
-                if clear then
-                    List.app (fn (_, r) => r := SOME empty) unifs1
-                else
-                    raise CUnify' (CRecordFailure (unsummarize s1, unsummarize s2))
-              | ((c1, _) :: rest1, (_, r2) :: rest2) =>
-                (r2 := SOME c1;
-                 pairOffUnifs (rest1, rest2))
-    in
-        pairOffUnifs (unifs1, unifs2)
-        (*before eprefaces "Summaries'" [("#1", p_summary env s1),
-                                      ("#2", p_summary env s2)]*)
-    end
-
-and guessFold (env, denv) (c1, c2, gs, ex) =
-    let
-        val loc = #2 c1
-
-        fun unfold (dom, ran, f, i, r, c) =
-            let
-                val nm = cunif (loc, (L'.KName, loc))
-                val v = cunif (loc, dom)
-                val rest = cunif (loc, (L'.KRecord dom, loc))
-                val acc = (L'.CFold (dom, ran), loc)
-                val acc = (L'.CApp (acc, f), loc)
-                val acc = (L'.CApp (acc, i), loc)
-                val acc = (L'.CApp (acc, rest), loc)
-
-                val (iS, gs3) = summarizeCon (env, denv) i
-
-                val app = (L'.CApp (f, nm), loc)
-                val app = (L'.CApp (app, v), loc)
-                val app = (L'.CApp (app, acc), loc)
-                val (appS, gs4) = summarizeCon (env, denv) app
-
-                val (cS, gs5) = summarizeCon (env, denv) c
-            in
-                (*prefaces "Summaries" [("iS", p_con_summary iS),
-                                        ("appS", p_con_summary appS),
-                                        ("cS", p_con_summary cS)];*)
-
-                if compatible (iS, appS) then
-                    raise ex
-                else if compatible (cS, iS) then
-                    let
-                        (*val () = prefaces "Same?" [("i", p_con env i),
-                                                   ("c", p_con env c)]*)
-                        val gs6 = unifyCons (env, denv) i c
-                        (*val () = TextIO.print "Yes!\n"*)
-
-                        val gs7 = unifyCons (env, denv) r (L'.CRecord (dom, []), loc)
-                    in
-                        gs @ gs3 @ gs5 @ gs6 @ gs7
-                    end
-                else if compatible (cS, appS) then
-                    let
-                        (*val () = prefaces "Same?" [("app", p_con env app),
-                                                     ("c", p_con env c),
-                                                     ("app'", p_con env (#1 (hnormCon (env, denv) app)))]*)
-                        val gs6 = unifyCons (env, denv) app c
-                        (*val () = TextIO.print "Yes!\n"*)
-
-                        val singleton = (L'.CRecord (dom, [(nm, v)]), loc)
-                        val concat = (L'.CConcat (singleton, rest), loc)
-                        (*val () = prefaces "Pre-crew" [("r", p_con env r),
-                                                        ("concat", p_con env concat)]*)
-                        val gs7 = unifyCons (env, denv) r concat
-                    in
-                        (*prefaces "The crew" [("nm", p_con env nm),
-                                               ("v", p_con env v),
-                                               ("rest", p_con env rest)];*)
-
-                        gs @ gs3 @ gs4 @ gs5 @ gs6 @ gs7
-                    end
-                else
-                    raise ex
-            end
-            handle _ => raise ex
-    in
-        case (#1 c1, #1 c2) of
-            (L'.CApp ((L'.CApp ((L'.CApp ((L'.CFold (dom, ran), _), f), _), i), _), r), _) =>
-            unfold (dom, ran, f, i, r, c2)
-          | (_, L'.CApp ((L'.CApp ((L'.CApp ((L'.CFold (dom, ran), _), f), _), i), _), r)) =>
-            unfold (dom, ran, f, i, r, c1)
-          | _ => raise ex
-    end
-
-and unifyCons' (env, denv) c1 c2 =
-    case (#1 c1, #1 c2) of
-        (L'.CDisjoint (_, x1, y1, t1), L'.CDisjoint (_, x2, y2, t2)) =>
-        let
-            val gs1 = unifyCons' (env, denv) x1 x2
-            val gs2 = unifyCons' (env, denv) y1 y2
-            val (denv', gs3) = D.assert env denv (x1, y1)
-            val gs4 = unifyCons' (env, denv') t1 t2
-        in
-            gs1 @ gs2 @ gs3 @ gs4
-        end
-      | _ =>
-        let
-            val (c1, gs1) = hnormCon (env, denv) c1
-            val (c2, gs2) = hnormCon (env, denv) c2
-        in
-            let
-                val gs3 = unifyCons'' (env, denv) c1 c2
-            in
-                gs1 @ gs2 @ gs3
-            end
-            handle ex => guessFold (env, denv) (c1, c2, gs1 @ gs2, ex)
-        end
-    
-and unifyCons'' (env, denv) (c1All as (c1, loc)) (c2All as (c2, _)) =
-    let
-        fun err f = raise CUnify' (f (c1All, c2All))
-
-        fun isRecord () = unifyRecordCons (env, denv) (c1All, c2All)
-    in
-        (*eprefaces "unifyCons''" [("c1All", p_con env c1All),
-                                 ("c2All", p_con env c2All)];*)
-
-        case (c1, c2) of
-            (L'.CUnit, L'.CUnit) => []
-
-          | (L'.TFun (d1, r1), L'.TFun (d2, r2)) =>
-            unifyCons' (env, denv) d1 d2
-            @ unifyCons' (env, denv) r1 r2
-          | (L'.TCFun (expl1, x1, d1, r1), L'.TCFun (expl2, _, d2, r2)) =>
-            if expl1 <> expl2 then
-                err CExplicitness
-            else
-                (unifyKinds d1 d2;
-                 unifyCons' (E.pushCRel env x1 d1, D.enter denv) r1 r2)
-          | (L'.TRecord r1, L'.TRecord r2) => unifyCons' (env, denv) r1 r2
-
-          | (L'.CRel n1, L'.CRel n2) =>
-            if n1 = n2 then
-                []
-            else
-                err CIncompatible
-          | (L'.CNamed n1, L'.CNamed n2) =>
-            if n1 = n2 then
-                []
-            else
-                err CIncompatible
-
-          | (L'.CApp (d1, r1), L'.CApp (d2, r2)) =>
-            (unifyCons' (env, denv) d1 d2;
-             unifyCons' (env, denv) r1 r2)
-          | (L'.CAbs (x1, k1, c1), L'.CAbs (_, k2, c2)) =>
-            (unifyKinds k1 k2;
-             unifyCons' (E.pushCRel env x1 k1, D.enter denv) c1 c2)
-
-          | (L'.CName n1, L'.CName n2) =>
-            if n1 = n2 then
-                []
-            else
-                err CIncompatible
-
-          | (L'.CModProj (n1, ms1, x1), L'.CModProj (n2, ms2, x2)) =>
-            if n1 = n2 andalso ms1 = ms2 andalso x1 = x2 then
-                []
-            else
-                err CIncompatible
-
-          | (L'.CTuple cs1, L'.CTuple cs2) =>
-            ((ListPair.foldlEq (fn (c1, c2, gs) =>
-                                   let
-                                       val gs' = unifyCons' (env, denv) c1 c2
-                                   in
-                                       gs' @ gs
-                                   end) [] (cs1, cs2))
-             handle ListPair.UnequalLengths => err CIncompatible)
-
-          | (L'.CProj ((L'.CUnif (_, _, _, ref (SOME c1)), loc), n1), _) =>
-            unifyCons' (env, denv) (L'.CProj (c1, n1), loc) c2All
-          | (_, L'.CProj ((L'.CUnif (_, _, _, ref (SOME c2)), loc), n2)) =>
-            unifyCons' (env, denv) c1All (L'.CProj (c2, n2), loc)
-          | (L'.CProj ((L'.CUnif (_, (L'.KTuple ks, _), _, r), loc), n), _) =>
-            let
-                val us = map (fn k => cunif (loc, k)) ks
-            in
-                r := SOME (L'.CTuple us, loc);
-                unifyCons' (env, denv) (List.nth (us, n - 1)) c2All
-            end
-          | (_, L'.CProj ((L'.CUnif (_, (L'.KTuple ks, _), _, r), loc), n)) =>
-            let
-                val us = map (fn k => cunif (loc, k)) ks
-            in
-                r := SOME (L'.CTuple us, loc);
-                unifyCons' (env, denv) c1All (List.nth (us, n - 1))
-            end
-          | (L'.CProj (c1, n1), L'.CProj (c2, n2)) =>
-            if n1 = n2 then
-                unifyCons' (env, denv) c1 c2
-            else
-                err CIncompatible
-
-          | (L'.CError, _) => []
-          | (_, L'.CError) => []
-
-          | (L'.CRecord _, _) => isRecord ()
-          | (_, L'.CRecord _) => isRecord ()
-          | (L'.CConcat _, _) => isRecord ()
-          | (_, L'.CConcat _) => isRecord ()
-
-          | (L'.CUnif (_, k1, _, r1), L'.CUnif (_, k2, _, r2)) =>
-            if r1 = r2 then
-                []
-            else
-                (unifyKinds k1 k2;
-                 r1 := SOME c2All;
-                 [])
-
-          | (L'.CUnif (_, _, _, r), _) =>
-            if occursCon r c2All then
-                err COccursCheckFailed
-            else
-                (r := SOME c2All;
-                 [])
-          | (_, L'.CUnif (_, _, _, r)) =>
-            if occursCon r c1All then
-                err COccursCheckFailed
-            else
-                (r := SOME c1All;
-                 [])
-
-          | (L'.CFold (dom1, ran1), L'.CFold (dom2, ran2)) =>
-            (unifyKinds dom1 dom2;
-             unifyKinds ran1 ran2;
+
+ fun checkPatCon (env, denv) p c1 c2 =
+     unifyCons (env, denv) c1 c2
+     handle CUnify (c1, c2, err) =>
+            (expError env (PatUnify (p, c1, c2, err));
              [])
 
-          | _ => err CIncompatible
-    end
-
-and unifyCons (env, denv) c1 c2 =
-    unifyCons' (env, denv) c1 c2
-    handle CUnify' err => raise CUnify (c1, c2, err)
-         | KUnify args => raise CUnify (c1, c2, CKind args)
-         
-fun checkCon (env, denv) e c1 c2 =
-    unifyCons (env, denv) c1 c2
-    handle CUnify (c1, c2, err) =>
-           (expError env (Unify (e, c1, c2, err));
-            [])
-
-fun checkPatCon (env, denv) p c1 c2 =
-    unifyCons (env, denv) c1 c2
-    handle CUnify (c1, c2, err) =>
-           (expError env (PatUnify (p, c1, c2, err));
-            [])
-
-fun primType env p =
-    case p of
-        P.Int _ => !int
-      | P.Float _ => !float
-      | P.String _ => !string
-
-fun recCons (k, nm, v, rest, loc) =
-    (L'.CConcat ((L'.CRecord (k, [(nm, v)]), loc),
-                 rest), loc)
-
-fun foldType (dom, loc) =
-    (L'.TCFun (L'.Explicit, "ran", (L'.KArrow ((L'.KRecord dom, loc), (L'.KType, loc)), loc),
-               (L'.TFun ((L'.TCFun (L'.Explicit, "nm", (L'.KName, loc),
-                                    (L'.TCFun (L'.Explicit, "v", dom,
-                                               (L'.TCFun (L'.Explicit, "rest", (L'.KRecord dom, loc),
-                                                          (L'.TFun ((L'.CApp ((L'.CRel 3, loc), (L'.CRel 0, loc)), loc),
-                                                                    (L'.CDisjoint (L'.Instantiate,
-                                                                                   (L'.CRecord
-                                                                                        ((L'.KUnit, loc),
-                                                                                         [((L'.CRel 2, loc),
-                                                                                           (L'.CUnit, loc))]), loc),
-                                                                                   (L'.CRel 0, loc),
-                                                                                   (L'.CApp ((L'.CRel 3, loc),
-                                                                                             recCons (dom,
-                                                                                                      (L'.CRel 2, loc),
-                                                                                                      (L'.CRel 1, loc),
-                                                                                                      (L'.CRel 0, loc),
-                                                                                                      loc)), loc)),
-                                                                     loc)), loc)),
-                                                loc)), loc)), loc),
-                         (L'.TFun ((L'.CApp ((L'.CRel 0, loc), (L'.CRecord (dom, []), loc)), loc),
-                                   (L'.TCFun (L'.Explicit, "r", (L'.KRecord dom, loc),
-                                              (L'.CApp ((L'.CRel 1, loc), (L'.CRel 0, loc)), loc)), loc)),
-                          loc)), loc)), loc)
-
-datatype constraint =
-         Disjoint of D.goal
-       | TypeClass of E.env * L'.con * L'.exp option ref * ErrorMsg.span
-
-val enD = map Disjoint
-
-fun elabHead (env, denv) (e as (_, loc)) t =
-    let
-        fun unravel (t, e) =
-            let
-                val (t, gs) = hnormCon (env, denv) t
-            in
-                case t of
-                    (L'.TCFun (L'.Implicit, x, k, t'), _) =>
-                    let
-                        val u = cunif (loc, k)
-
-                        val t'' = subConInCon (0, u) t'
-                        val (e, t, gs') = unravel (t'', (L'.ECApp (e, u), loc))
-                    in
-                        (*prefaces "Unravel" [("t'", p_con env t'),
-                                            ("t''", p_con env t'')];*)
-                        (e, t, enD gs @ gs')
-                    end
+ fun primType env p =
+     case p of
+         P.Int _ => !int
+       | P.Float _ => !float
+       | P.String _ => !string
+
+ fun recCons (k, nm, v, rest, loc) =
+     (L'.CConcat ((L'.CRecord (k, [(nm, v)]), loc),
+                  rest), loc)
+
+ fun foldType (dom, loc) =
+     (L'.TCFun (L'.Explicit, "ran", (L'.KArrow ((L'.KRecord dom, loc), (L'.KType, loc)), loc),
+                (L'.TFun ((L'.TCFun (L'.Explicit, "nm", (L'.KName, loc),
+                                     (L'.TCFun (L'.Explicit, "v", dom,
+                                                (L'.TCFun (L'.Explicit, "rest", (L'.KRecord dom, loc),
+                                                           (L'.TFun ((L'.CApp ((L'.CRel 3, loc), (L'.CRel 0, loc)), loc),
+                                                                     (L'.CDisjoint (L'.Instantiate,
+                                                                                    (L'.CRecord
+                                                                                         ((L'.KUnit, loc),
+                                                                                          [((L'.CRel 2, loc),
+                                                                                            (L'.CUnit, loc))]), loc),
+                                                                                    (L'.CRel 0, loc),
+                                                                                    (L'.CApp ((L'.CRel 3, loc),
+                                                                                              recCons (dom,
+                                                                                                       (L'.CRel 2, loc),
+                                                                                                       (L'.CRel 1, loc),
+                                                                                                       (L'.CRel 0, loc),
+                                                                                                       loc)), loc)),
+                                                                      loc)), loc)),
+                                                 loc)), loc)), loc),
+                          (L'.TFun ((L'.CApp ((L'.CRel 0, loc), (L'.CRecord (dom, []), loc)), loc),
+                                    (L'.TCFun (L'.Explicit, "r", (L'.KRecord dom, loc),
+                                               (L'.CApp ((L'.CRel 1, loc), (L'.CRel 0, loc)), loc)), loc)),
+                           loc)), loc)), loc)
+
+ datatype constraint =
+          Disjoint of D.goal
+        | TypeClass of E.env * L'.con * L'.exp option ref * ErrorMsg.span
+
+ val enD = map Disjoint
+
+ fun elabHead (env, denv) infer (e as (_, loc)) t =
+     let
+         fun unravel (t, e) =
+             let
+                 val (t, gs) = hnormCon (env, denv) t
+             in
+                 case t of
+                     (L'.TCFun (L'.Implicit, x, k, t'), _) =>
+                     let
+                         val u = cunif (loc, k)
+
+                         val t'' = subConInCon (0, u) t'
+                         val (e, t, gs') = unravel (t'', (L'.ECApp (e, u), loc))
+                     in
+                         (*prefaces "Unravel" [("t'", p_con env t'),
+                                             ("t''", p_con env t'')];*)
+                         (e, t, enD gs @ gs')
+                     end
+                   | (L'.TFun (dom as (L'.CApp (cl, _), _), ran), _) =>
+                     let
+                         val cl = #1 (hnormCon (env, denv) cl)
+                     in
+                         if infer <> L.TypesOnly andalso E.isClass env cl then
+                             let
+                                 val r = ref NONE
+                                 val (e, t, gs') = unravel (ran, (L'.EApp (e, (L'.EUnif r, loc)), loc))
+                             in
+                                 (e, t, TypeClass (env, dom, r, loc) :: enD gs @ gs')
+                             end
+                         else
+                             (e, t, enD gs)
+                     end
                   | _ => (e, t, enD gs)
             end
     in
-        unravel (t, e)
+        case infer of
+            L.DontInfer => (e, t, [])
+          | _ => unravel (t, e)
     end
 
 fun elabPat (pAll as (p, loc), (env, denv, bound)) =
@@ -1393,18 +1409,14 @@
             end
 
           | L.EPrim p => ((L'.EPrim p, loc), primType env p, [])
-          | L.EVar ([], s) =>
+          | L.EVar ([], s, infer) =>
             (case E.lookupE env s of
                  E.NotBound =>
                  (expError env (UnboundExp (loc, s));
                   (eerror, cerror, []))
-               | E.Rel (n, t) => ((L'.ERel n, loc), t, [])
-               | E.Named (n, t) =>
-                 if Char.isUpper (String.sub (s, 0)) then
-                     elabHead (env, denv) (L'.ENamed n, loc) t
-                 else
-                     ((L'.ENamed n, loc), t, []))
-          | L.EVar (m1 :: ms, s) =>
+               | E.Rel (n, t) => elabHead (env, denv) infer (L'.ERel n, loc) t
+               | E.Named (n, t) => elabHead (env, denv) infer (L'.ENamed n, loc) t)
+          | L.EVar (m1 :: ms, s, infer) =>
             (case E.lookupStr env m1 of
                  NONE => (expError env (UnboundStrInExp (loc, m1));
                           (eerror, cerror, []))
@@ -1422,7 +1434,7 @@
                                           cerror)
                                | SOME t => t
                  in
-                     ((L'.EModProj (n, ms, s), loc), t, [])
+                     elabHead (env, denv) infer (L'.EModProj (n, ms, s), loc) t
                  end)
 
           | L.EWild =>
@@ -1436,17 +1448,16 @@
           | L.EApp (e1, e2) =>
             let
                 val (e1', t1, gs1) = elabExp (env, denv) e1
-                val (e1', t1, gs2) = elabHead (env, denv) e1' t1
-                val (e2', t2, gs3) = elabExp (env, denv) e2
+                val (e2', t2, gs2) = elabExp (env, denv) e2
 
                 val dom = cunif (loc, ktype)
                 val ran = cunif (loc, ktype)
                 val t = (L'.TFun (dom, ran), dummy)
 
-                val gs4 = checkCon (env, denv) e1' t1 t
-                val gs5 = checkCon (env, denv) e2' t2 dom
-
-                val gs = gs1 @ gs2 @ gs3 @ enD gs4 @ enD gs5
+                val gs3 = checkCon (env, denv) e1' t1 t
+                val gs4 = checkCon (env, denv) e2' t2 dom
+
+                val gs = gs1 @ gs2 @ enD gs3 @ enD gs4
             in
                 ((L'.EApp (e1', e2'), loc), ran, gs)
             end
@@ -1472,9 +1483,8 @@
             let
                 val (e', et, gs1) = elabExp (env, denv) e
                 val oldEt = et
-                val (e', et, gs2) = elabHead (env, denv) e' et
-                val (c', ck, gs3) = elabCon (env, denv) c
-                val ((et', _), gs4) = hnormCon (env, denv) et
+                val (c', ck, gs2) = elabCon (env, denv) c
+                val ((et', _), gs3) = hnormCon (env, denv) et
             in
                 case et' of
                     L'.CError => (eerror, cerror, [])
@@ -1491,7 +1501,7 @@
                                                ("eb", p_con (E.pushCRel env x k) eb),
                                                ("c", p_con env c'),
                                                ("eb'", p_con env eb')];*)
-                        ((L'.ECApp (e', c'), loc), eb', gs1 @ gs2 @ enD gs3 @ enD gs4)
+                        ((L'.ECApp (e', c'), loc), eb', gs1 @ enD gs2 @ enD gs3)
                     end
 
                   | _ =>