[MLton-commit] r6229

Matthew Fluet fluet at mlton.org
Thu Nov 29 12:08:20 PST 2007


Fixed bug in type inference of flexible records.  This would later
cause the compiler to raise the TypeError exception when type checking
the XML program after the defunctorize pass.

The following program exhibits the bug:

fun f (a, b) = (#value a) < (#value b)

fun g (array1 (* : {value:nat, offset:'a1} array *), 
       array2 (* : {value:nat, offset:'a2} array *),
       record1 : {value:nat, offset:'b},
       record2 : {value:nat, offset:'c}) =
   let
      val left (* : {value:nat, ...} *) = Array.sub (array1, 0)
      val right (* : {value:nat, ...} *) = Array.sub (array1, 1)
      val small (* : {value:nat, ...} *) = 
         if f (left, right)
           then Array.sub (array2, 0)
           else Array.sub (array2, 1)
   in
      (f (record1, small),
       f (small, record2))
   end

After elaborating "fun f (a, b) = ...", f is assigned the type scheme:
  {value:int, gen1:...} * {value:int, gen2:...} -> bool
where gen1 and gen2 denote (distinct) generalizable flex records, each
with their own spine.

After elaborating "val left = ..." and "val right = ...", left and
right are assigned (the same) unification variable, which is otherwise
unconstrained.

In elaborating "f (left, right)", we instantiate the type scheme of f,
realizing the two generalizable flex records as flex records.  We then
unify the flex records with left and with right.  Since left and right
have the same unification variable, this effectively unifies the two
flex records, which unifies the two spines.  This is correct, as we
now know that the spines of the two record arguments to f must be the
same.  However, the generalizable fields may be generalized to
different types in the two arguments.  But, Spine.unify causes the two
spines for the record arguments to be equal according to Spine.equals.
This causes problems for computing the type arguments to an
application of f, because we use Spine.equals to find the flexible
record type that was derived from the generalizeable flex record at
scheme instantiation.  Nothing goes wrong with "f (left, right)",
because left and right have the same type, so the instantiation of the
offset field type will be the same.  However, with "f (record1,
small)" the instantiations of the offset field type will be different
('b for record1, and 'a2 for small).  The spine unification caused the
lookup of the type argument from the generalizable fields of the first
record to see the types of the second record, leading to the XML type
error:
  Type error: actual and formal not of same type
  actual: ('a_4092 * word32) * (word32 * word32)
  formal: (word32 * word32) * (word32 * word32)

The fix was to give Spine.t's an identity separate from the
DisjointSet.t used to ensure that spines had the same sets of fields.
The spine identity is now used for Spine.equals in the lookup.


----------------------------------------------------------------------

U   mlton/trunk/doc/changelog
U   mlton/trunk/mlton/elaborate/type-env.fun

----------------------------------------------------------------------

Modified: mlton/trunk/doc/changelog
===================================================================
--- mlton/trunk/doc/changelog	2007-11-29 15:30:10 UTC (rev 6228)
+++ mlton/trunk/doc/changelog	2007-11-29 20:08:19 UTC (rev 6229)
@@ -1,5 +1,10 @@
 Here are the changes from version 20070826 to version YYYYMMDD.
 
+* 2007-11-29
+   - Fixed bug in type inference of flexible records.  This would
+     later cause the compiler to raise the TypeError exception.
+     Thanks to Wesley Terpstra for the bug report.
+
 * 2007-11-28
    - Fixed bug in cross-compilation of gdtoa library.  Thanks to
      Wesley Terpstra for the bug report and patch.

Modified: mlton/trunk/mlton/elaborate/type-env.fun
===================================================================
--- mlton/trunk/mlton/elaborate/type-env.fun	2007-11-29 15:30:10 UTC (rev 6228)
+++ mlton/trunk/mlton/elaborate/type-env.fun	2007-11-29 20:08:19 UTC (rev 6229)
@@ -316,15 +316,23 @@
       val unify: t * t -> unit
    end =
    struct
-      datatype t = T of {fields: Field.t list ref,
-                         more: bool ref} Set.t
+      datatype t = T of {id: int,
+                         body: {fields: Field.t list ref,
+                                more: bool ref} Set.t}
 
-      fun new fields = T (Set.singleton {fields = ref fields,
-                                         more = ref true})
+      local
+         val r: int ref = ref 0
+      in
+         fun newId () = (Int.inc r; !r)
+      end
+      
+      fun new fields = T {id = newId (),
+                          body = Set.singleton {fields = ref fields,
+                                                more = ref true}}
 
-      fun equals (T s, T s') = Set.equals (s, s')
+      fun equals (T {id = id1,...}, T {id = id2,...}) = id1 = id2
 
-      fun layout (T s) =
+      fun layout (T {body = s,...}) =
          let
             val {fields, more} = Set.! s
          in
@@ -332,23 +340,25 @@
                            ("more", Bool.layout (!more))]
          end
 
-      fun canAddFields (T s) = ! (#more (Set.! s))
-      fun fields (T s) = ! (#fields (Set.! s))
+      fun canAddFields (T {body = s,...}) = ! (#more (Set.! s))
+      fun fields (T {body = s,...}) = ! (#fields (Set.! s))
 
       fun ensureFieldValue ({fields, more}, f) =
          List.contains (!fields, f, Field.equals)
          orelse (!more andalso (List.push (fields, f); true))
 
-      fun ensureField (T s, f) = ensureFieldValue (Set.! s, f)
+      fun ensureField (T {body = s,...}, f) = ensureFieldValue (Set.! s, f)
 
-      fun noMoreFields (T s) = #more (Set.! s) := false
+      fun noMoreFields (T {body = s,...}) = #more (Set.! s) := false
 
-      fun unify (T s, T s') =
+      fun unify (T {body = s1,...}, T {body = s2,...}) =
          let
-            val {fields = fs, more = m} = Set.! s
-            val {more = m', ...} = Set.! s'
-            val _ = Set.union (s, s')
-            val _ = Set.:= (s, {fields = fs, more = ref (!m andalso !m')})
+            val {fields = fs1, more = m1} = Set.! s1
+            val {fields = fs2, more = m2} = Set.! s2
+            val _ = Set.union (s1, s2)
+            val fs = List.union (!fs1, !fs2, Field.equals)
+            val m = !m1 andalso !m2
+            val _ = Set.:= (s1, {fields = ref fs, more = ref m})
          in
             ()
          end




More information about the MLton-commit mailing list