shithub: mc

Download patch

ref: 4a8f78d53d61423fdeb6c59881661d6cd5cbd430
parent: 5c477090a5c2afbd60ccbe6e078cc94a73f4c08b
author: Ori Bernstein <[email protected]>
date: Sun Sep 28 13:01:40 EDT 2014

Fix premature unification in generics.

    We used to unify generics prematurely, meaning that if we had
    a generic being used before the declaration was fully inferred,
    we would fix it's type at the first use.

    This would cause errors when we tried to use it with a different
    type. A demonstration of this bug is below:

        const main = {
            id("asdf")  /* we decided that id (byte[:] -> $t) */
            id(123)     /* type error: (byte[:] -> $t) is not compatible
                           with (intline -> $t) */
        }

        generic id = {x : @a
            -> x
        }

--- a/parse/dump.c
+++ b/parse/dump.c
@@ -233,3 +233,8 @@
 {
     outnode(n, fd, 0);
 }
+
+void dumpn(Node *n)
+{
+    dump(n, stdout);
+}
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -104,6 +104,16 @@
     return s;
 }
 
+static void addspecialization(Inferstate *st, Node *n, Stab *stab)
+{
+    Node *dcl;
+
+    dcl = decls[n->expr.did];
+    lappend(&st->specializationscope, &st->nspecializationscope, stab);
+    lappend(&st->specializations, &st->nspecializations, n);
+    lappend(&st->genericdecls, &st->ngenericdecls, dcl);
+}
+
 static void delayedcheck(Inferstate *st, Node *n, Stab *s)
 {
     lappend(&st->postcheck, &st->npostcheck, n);
@@ -1002,13 +1012,18 @@
         t = tyfreshen(st, tf(st, s->decl.type));
     else
         t = s->decl.type;
-    settype(st, n, t);
     n->expr.did = s->decl.did;
     n->expr.isconst = s->decl.isconst;
     if (s->decl.isgeneric && !st->ingeneric) {
-        lappend(&st->specializationscope, &st->nspecializationscope, curstab());
-        lappend(&st->specializations, &st->nspecializations, n);
-        lappend(&st->genericdecls, &st->ngenericdecls, s);
+        addspecialization(st, n, curstab());
+        if (t->type == Tyvar) {
+            settype(st, n, mktyvar(n->line));
+            delayedcheck(st, n, curstab());
+        } else {
+            settype(st, n, t);
+        }
+    } else { 
+        settype(st, n, t);
     }
     return t;
 }
@@ -1800,6 +1815,14 @@
     }
 }
 
+static void checkvar(Inferstate *st, Node *n)
+{
+    Node *dcl;
+
+    dcl = decls[n->expr.did];
+    unify(st, n, type(st, n), tyfreshen(st, type(st, dcl)));
+}
+
 static void postcheck(Inferstate *st, Node *file)
 {
     size_t i;
@@ -1814,6 +1837,8 @@
             checkcast(st, n);
         else if (n->type == Nexpr && exprop(n) == Ostruct)
             checkstruct(st, n);
+        else if (n->type == Nexpr && exprop(n) == Ovar)
+            checkvar(st, n);
         else
             die("Thing we shouldn't be checking in postcheck\n");
         popstab();
--- a/test/genericcall.myr
+++ b/test/genericcall.myr
@@ -4,12 +4,12 @@
 	-> 42
 }
 
-generic id = {a:@a
-	-> f()
-}
-
 const main = {
 	id("adsf")
 	std.exit(id(42))
+}
+
+generic id = {a:@a
+	-> f()
 }