shithub: mc

Download patch

ref: e802a58426b982f88d2dba72fb6762d1ae2ed8f8
parent: a3b2adffd9eeb3d15e54cfcba7b1f1097ade002d
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Mar 8 20:57:24 EDT 2015

Make type substitution more robust.

    Break out Tyname into Tygeneric and Tyname. Tygeneric
    acts like a function returning a fully substituted Tyname.

--- a/parse/gram.y
+++ b/parse/gram.y
@@ -404,7 +404,11 @@
 tydef   : Ttype typeid {$$ = $2;}
         | Ttype typeid Tasn type {
                 $$ = $2;
-                $$.type = mktyname($2.loc, mkname($2.loc, $2.name), $2.params, $2.nparams, $4);
+                if ($$.nparams == 0) {
+                    $$.type = mktyname($2.loc, mkname($2.loc, $2.name), $4);
+                } else {
+                    $$.type = mktygeneric($2.loc, mkname($2.loc, $2.name), $2.params, $2.nparams, $4);
+                }
             }
         ;
 
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -80,7 +80,7 @@
         if (et != NULL)
             t = tystr(et);
         else
-            t = "?";
+            t = strdup("?");
 
         if (exprop(args[i]) == Ovar)
             p += snprintf(p, end - p, "%s%s:%s", sep, namestr(args[0]->expr.args[0]), t);
@@ -293,26 +293,34 @@
 }
 
 
-static int needfreshen(Inferstate *st, Type *t)
+static int needfreshenrec(Inferstate *st, Type *t, Bitset *visited)
 {
     size_t i;
 
+    if (bshas(visited, t->tid))
+        return 0;
+    bsput(visited, t->tid);
     switch (t->type) {
         case Typaram:   return 1;
-        case Tyname:    return isgeneric(t);
+        case Tygeneric: return 1;
+        case Tyname:
+            for (i = 0; i < t->narg; i++)
+                if (needfreshenrec(st, t->arg[i], visited))
+                    return 1;
+            return needfreshenrec(st, t->sub[0], visited);
         case Tystruct:
             for (i = 0; i < t->nmemb; i++)
-                if (needfreshen(st, decltype(t->sdecls[i])))
+                if (needfreshenrec(st, decltype(t->sdecls[i]), visited))
                     return 1;
             break;
         case Tyunion:
             for (i = 0; i < t->nmemb; i++)
-                if (t->udecls[i]->etype && needfreshen(st, t->udecls[i]->etype))
+                if (t->udecls[i]->etype && needfreshenrec(st, t->udecls[i]->etype, visited))
                     return 1;
             break;
         default:
             for (i = 0; i < t->nsub; i++)
-                if (needfreshen(st, t->sub[i]))
+                if (needfreshenrec(st, t->sub[i], visited))
                     return 1;
             break;
     }
@@ -319,10 +327,20 @@
     return 0;
 }
 
+static int needfreshen(Inferstate *st, Type *t)
+{
+    Bitset *visited;
+    int ret;
+
+    visited = mkbs();
+    ret = needfreshenrec(st, t, visited);
+    bsfree(visited);
+    return ret;
+}
+
 /* Freshens the type of a declaration. */
-static Type *tyfreshen(Inferstate *st, Type *t)
+static Type *_tyfreshen(Inferstate *st, Htab *subst, Type *t)
 {
-    Htab *ht;
     char *from, *to;
 
     if (!needfreshen(st, t)) {
@@ -333,9 +351,13 @@
 
     from = tystr(t);
     tybind(st, t);
-    ht = mkht(tyhash, tyeq);
-    t = tyspecialize(t, ht, st->delayed);
-    htfree(ht);
+    if (!subst) {
+        subst = mkht(tyhash, tyeq);
+        t = tyspecialize(t, subst, st->delayed);
+        htfree(subst);
+    } else {
+        t = tyspecialize(t, subst, st->delayed);
+    }
     tyunbind(st, t);
     if (debugopt['u']) {
         to = tystr(t);
@@ -426,29 +448,41 @@
     return t;
 }
 
+static Type *tysubst(Inferstate *st, Type *t, Type *orig)
+{
+    Htab *subst;
+    size_t i;
+
+    subst = mkht(tyhash, tyeq);
+    for (i = 0; i < t->ngparam; i++) {
+        htput(subst, t->gparam[i], orig->arg[i]);
+    }
+    t = _tyfreshen(st, subst, t);
+    htfree(subst);
+    return t;
+}
+
 /* fixd the most accurate type mapping we have (ie,
  * the end of the unification chain */
 static Type *tf(Inferstate *st, Type *orig)
 {
+    int isgeneric;
     Type *t;
-    size_t i;
 
     t = tysearch(orig);
-    st->ingeneric += isgeneric(orig);
+    isgeneric = t->type == Tygeneric;
+    st->ingeneric += isgeneric;
     tyresolve(st, t);
     /* If this is an instantiation of a generic type, we want the params to
      * match the instantiation */
-    if (orig->type == Tyunres && isgeneric(t)) {
-        if (t->nparam != orig->narg) {
+    if (orig->type == Tyunres && t->type == Tygeneric) {
+        if (t->ngparam != orig->narg) {
             lfatal(orig->loc, "%s incompatibly specialized with %s, declared on %s:%d",
                    tystr(orig), tystr(t), file->file.files[t->loc.file], t->loc.line);
         }
-        t = tyfreshen(st, t);
-        for (i = 0; i < t->narg; i++) {
-            unify(st, NULL, t->arg[i], orig->arg[i]);
-        }
+        t = tysubst(st, t, orig);
     }
-    st->ingeneric -= isgeneric(orig);
+    st->ingeneric -= isgeneric;
     return t;
 }
 
@@ -576,7 +610,7 @@
     Htab *bt;
     char *s;
 
-    if (t->type != Tyname && !isgeneric(t))
+    if (t->type != Tygeneric)
         return;
     if (debugopt['u']) {
         s = tystr(t);
@@ -621,7 +655,7 @@
 
 static void tyunbind(Inferstate *st, Type *t)
 {
-    if (t->type != Tyname && !isgeneric(t))
+    if (t->type != Tygeneric)
         return;
     htfree(st->tybindings[st->ntybindings - 1]);
     lpop(&st->tybindings, &st->ntybindings);
@@ -954,12 +988,13 @@
     if (s->decl.ishidden)
         fatal(n, "attempting to refer to hidden decl %s", ctxstr(st, n));
     if (s->decl.isgeneric)
-        t = tyfreshen(st, tf(st, s->decl.type));
+        t = tysubst(st, tf(st, s->decl.type), s->decl.type);
     else
         t = s->decl.type;
     n->expr.did = s->decl.did;
     n->expr.isconst = s->decl.isconst;
     if (s->decl.isgeneric && !st->ingeneric) {
+        t = _tyfreshen(st, NULL, t);
         addspecialization(st, n, curstab());
         if (t->type == Tyvar) {
             settype(st, n, mktyvar(n->loc));
@@ -1067,7 +1102,7 @@
 
     *isconst = 1;
     uc = uconresolve(st, n);
-    t = tyfreshen(st, tf(st, uc->utype));
+    t = tysubst(st, tf(st, uc->utype), uc->utype);
     uc = tybase(t)->udecls[uc->id];
     if (uc->etype) {
         inferexpr(st, &n->expr.args[1], NULL, NULL);
@@ -1123,7 +1158,7 @@
             s = getdcl(ns, args[0]);
             if (s && !s->decl.ishidden) {
                 if (s->decl.isgeneric)
-                    t = tyfreshen(st, s->decl.type);
+                    t = tysubst(st, s->decl.type, s->decl.type);
                 else if (s->decl.isconst)
                     t = s->decl.type;
                 else
@@ -1449,7 +1484,7 @@
                   namestr(dcl->decl.name), namestr(t->name), ctxstr(st, n));
 
         /* infer and unify types */
-        if (isgeneric(n->impl.type))
+        if (n->impl.type->type == Tygeneric || n->impl.type->type == Typaram)
             fatal(n, "trait specialization requires concrete type, got %s", tystr(n->impl.type));
         checktraits(t->param, n->impl.type);
         ht = mkht(tyhash, tyeq);
@@ -1477,8 +1512,8 @@
     Type *t;
 
     t = tf(st, decltype(n));
-    if (t->type == Tyname && isgeneric(t) && !n->decl.isgeneric) {
-        t = tyfreshen(st, t);
+    if (t->type == Tygeneric && !n->decl.isgeneric) {
+        t = _tyfreshen(st, NULL, t);
         unifyparams(st, n, t, decltype(n));
     }
     settype(st, n, t);
@@ -1772,7 +1807,7 @@
     Node *dcl;
 
     dcl = decls[n->expr.did];
-    unify(st, n, type(st, n), tyfreshen(st, type(st, dcl)));
+    unify(st, n, type(st, n), _tyfreshen(st, NULL, type(st, dcl)));
 }
 
 static void postcheck(Inferstate *st, Node *file)
@@ -1975,8 +2010,9 @@
         case Tyname:
             for (i = 0; i < t->narg; i++)
                 taghidden(t->arg[i]);
-            for (i = 0; i < t->nparam; i++)
-                taghidden(t->param[i]);
+        case Tygeneric:
+            for (i = 0; i < t->ngparam; i++)
+                taghidden(t->gparam[i]);
             break;
         default:
             break;
@@ -2129,10 +2165,12 @@
         taghidden(t);
         for (j = 0; j < t->nsub; j++)
             taghidden(t->sub[j]);
-        for (j = 0; j < t->narg; j++)
-            taghidden(t->arg[j]);
-        for (j = 0; j < t->nparam; j++)
-            taghidden(t->param[j]);
+        if (t->type == Tyname)
+            for (j = 0; j < t->narg; j++)
+                taghidden(t->arg[j]);
+        if (t->type == Tygeneric)
+            for (j = 0; j < t->ngparam; j++)
+                taghidden(t->gparam[j]);
     }
     free(k);
 
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -147,8 +147,8 @@
     Node **traitlist;    /* The names of the constraints on the type. Used to fill the bitset */
     size_t ntraitlist;   /* The length of the constraint list above */
 
-    Type **param;       /* Tyname: type parameters that match the type args */
-    size_t nparam;      /* Tyname: count of type parameters */
+    Type **gparam;       /* Tygeneric: type parameters that match the type args */
+    size_t ngparam;      /* Tygeneric: count of type parameters */
     Type **arg;         /* Tyname: type arguments instantiated */
     size_t narg;        /* Tyname: count of type arguments */
     Type **inst;        /* Tyname: instances created */
@@ -483,7 +483,8 @@
 Type *tydup(Type *t); /* shallow duplicate; all subtypes/members/... kept */
 Type *mktyvar(Srcloc l);
 Type *mktyparam(Srcloc l, char *name);
-Type *mktyname(Srcloc l, Node *name, Type **params, size_t nparams, Type *base);
+Type *mktygeneric(Srcloc l, Node *name, Type **params, size_t nparams, Type *base);
+Type *mktyname(Srcloc l, Node *name, Type *base);
 Type *mktyunres(Srcloc l, Node *name, Type **params, size_t nparams);
 Type *mktyarray(Srcloc l, Type *base, Node *sz);
 Type *mktyslice(Srcloc l, Type *base);
@@ -500,7 +501,6 @@
 int   istyunsigned(Type *t);
 int   istyfloat(Type *t);
 int   istyprimitive(Type *t);
-int   isgeneric(Type *t);
 int   hasparams(Type *t);
 
 /* type manipulation */
--- a/parse/specialize.c
+++ b/parse/specialize.c
@@ -34,9 +34,8 @@
  * against */
 Type *tyspecialize(Type *t, Htab *tsmap, Htab *delayed)
 {
-    Type *ret, *tmp;
-    size_t i;
-    Type **subst;
+    Type *ret, *tmp, **arg;
+    size_t i, narg;
 
     t = tysearch(t);
     if (hthas(tsmap, t))
@@ -47,24 +46,22 @@
             addtraits(ret, t->traits);
             htput(tsmap, t, ret);
             break;
-        case Tyname:
-            if (t->narg)
-                subst = t->arg;
-            else
-                subst = t->param;
-            for (i = 0; i < t->nparam; i++) {
-                if (subst[i]->type != Typaram || hthas(tsmap, subst[i]))
-                    continue;
-                tmp = mktyvar(subst[i]->loc);
-                addtraits(tmp, subst[i]->traits);
-                htput(tsmap, subst[i], tmp);
-            }
-            ret = mktyname(t->loc, t->name, t->param, t->nparam, tyspecialize(t->sub[0], tsmap, delayed));
+        case Tygeneric:
+            ret = mktyname(t->loc, t->name, tyspecialize(t->sub[0], tsmap, delayed));
             ret->issynth = 1;
             htput(tsmap, t, ret);
-            for (i = 0; i < t->nparam; i++)
-                lappend(&ret->arg, &ret->narg, tyspecialize(subst[i], tsmap, delayed));
+            for (i = 0; i < t->ngparam; i++)
+                lappend(&ret->arg, &ret->narg, tyspecialize(t->gparam[i], tsmap, delayed));
             break;
+        case Tyname:
+            arg = NULL;
+            narg = 0;
+            for (i = 0; i < t->narg; i++)
+                lappend(&arg, &narg, tyspecialize(t->arg[i], tsmap, delayed));
+            ret = mktyname(t->loc, t->name, tyspecialize(t->sub[0], tsmap, delayed));
+            ret->arg = arg;
+            ret->narg = narg;
+            break;
         case Tystruct:
             ret = tydup(t);
             htput(tsmap, t, ret);
@@ -137,10 +134,9 @@
     assert(to->nsub == from->nsub);
     for (i = 0; i < to->nsub; i++)
         fillsubst(tsmap, to->sub[i], from->sub[i]);
-    if (to->type == Tyname && to->nparam > 0) {
-        assert(to->nparam == to->narg);
-        for (i = 0; i < to->nparam; i++)
-            fillsubst(tsmap, to->arg[i], to->param[i]);
+    if (to->type == Tyname && to->narg > 0) {
+        for (i = 0; i < to->narg; i++)
+            fillsubst(tsmap, to->arg[i], from->arg[i]);
     }
 }
 
--- a/parse/stab.c
+++ b/parse/stab.c
@@ -281,7 +281,9 @@
 
 int mergetype(Type *old, Type *new)
 {
-    if (old->vis == Visexport && new->vis != Visexport) {
+    if (!new) {
+        lfatal(new->loc, "double prototyping of %s", tystr(new));
+    } else if (old->vis == Visexport && new->vis != Visexport) {
         if (!old->sub && new->sub) {
             old->sub = new->sub;
             old->nsub = new->nsub;
@@ -304,7 +306,7 @@
 
     if (st->_name)
         setns(n, st->_name);
-    if (st->_name && t && t->type == Tyname)
+    if (st->_name && t && t->name)
         setns(t->name, st->_name);
 
     ty = gettype(st, n);
@@ -435,7 +437,7 @@
         setns(k[i], name);
     for (i = 0; i < nk; i++) {
         td = htget(st->ty, k[i]);
-        if (td->type && td->type->type == Tyname)
+        if (td->type && (td->type->type == Tyname || td->type->type == Tygeneric))
             setns(td->type->name, name);
     }
     free(k);
--- a/parse/type.c
+++ b/parse/type.c
@@ -27,6 +27,7 @@
 
 /* Built in type constraints */
 static Trait *traits[Ntypes + 1][4];
+static int tybfmt(char *buf, size_t len, Type *t);
 
 Type *mktype(Srcloc loc, Ty ty)
 {
@@ -164,10 +165,25 @@
     return t;
 }
 
-Type *mktyname(Srcloc loc, Node *name, Type **param, size_t nparam, Type *base)
+Type *mktygeneric(Srcloc loc, Node *name, Type **param, size_t nparam, Type *base)
 {
     Type *t;
 
+    t = mktype(loc, Tygeneric);
+    t->name = name;
+    t->nsub = 1;
+    t->traits = bsdup(base->traits);
+    t->sub = xalloc(sizeof(Type*));
+    t->sub[0] = base;
+    t->gparam = param;
+    t->ngparam = nparam;
+    return t;
+}
+
+Type *mktyname(Srcloc loc, Node *name, Type *base)
+{
+    Type *t;
+
     t = mktype(loc, Tyname);
     t->name = name;
     t->nsub = 1;
@@ -174,8 +190,6 @@
     t->traits = bsdup(base->traits);
     t->sub = xalloc(sizeof(Type*));
     t->sub[0] = base;
-    t->param = param;
-    t->nparam = nparam;
     return t;
 }
 
@@ -323,26 +337,6 @@
     return istysigned(t) || istyunsigned(t) || istyfloat(t);
 }
 
-int isgeneric(Type *t)
-{
-    size_t i;
-
-    if (t->type != Tyname && t->type != Tyunres)
-        return 0;
-    /*
-    if we have no arguments passed in, and we have parameters
-    we have a type of the form
-    type t(@a,...) = ...
-     */
-    if (!t->narg)
-        return t->nparam > 0;
-    else
-        for (i = 0; i < t->narg; i++)
-            if (hasparams(t->arg[i]))
-                return 1;
-    return 0;
-}
-
 /*
  * Checks if a type contains any type
  * parameers at all (ie, if it generic).
@@ -351,12 +345,19 @@
 {
     size_t i;
 
+    if (bshas(visited, t->tid))
+        return 0;
+    bsput(visited, t->tid);
     switch (t->type) {
         case Typaram:
+        case Tygeneric:
             return 1;
         case Tyname:
         case Tyunres:
-            return isgeneric(t);
+            for (i = 0; i < t->narg; i++)
+                if (hasparamsrec(t->arg[i], visited))
+                    return 1;
+            return hasparamsrec(t->sub[0], visited);
         case Tystruct:
             for (i = 0; i < t->nmemb; i++)
                 if (hasparamsrec(t->sdecls[i]->decl.type, visited))
@@ -390,7 +391,7 @@
 Type *tybase(Type *t)
 {
     assert(t != NULL);
-    while (t->type == Tyname)
+    while (t->type == Tyname || t->type == Tygeneric)
         t = t->sub[0];
     return t;
 }
@@ -488,6 +489,24 @@
     return p - buf;
 }
 
+static int fmtlist(char *buf, size_t len, Type **arg, size_t narg)
+{
+    char *end, *p, *sep;
+    size_t i;
+
+    sep = "";
+    p = buf;
+    end = p + len;
+    p += snprintf(p, end - p, "(");
+    for (i = 0; i < narg; i++)  {
+        p += snprintf(p, end - p, "%s", sep);
+        p += tybfmt(p, end - p, arg[i]);
+        sep = ", ";
+    }
+    p += snprintf(p, end - p, ")");
+    return p - buf;
+}
+
 static int tybfmt(char *buf, size_t len, Type *t)
 {
     size_t i;
@@ -494,12 +513,10 @@
     char *p;
     char *end;
     char *sep;
-    size_t narg;
-    Type **arg;
 
+    sep = "";
     p = buf;
     end = p + len;
-    sep = "";
     if (!t) {
         p += snprintf(p, end - p, "tynil");
         return len - (end - p);
@@ -575,38 +592,22 @@
             break;
         case Tyunres:
             p += namefmt(p, end - p, t->name);
-            if (t->narg) {
-                p += snprintf(p, end - p, "(");
-                for (i = 0; i < t->narg; i++)  {
-                    p += snprintf(p, end - p, "%s", sep);
-                    p += tybfmt(p, end - p, t->arg[i]);
-                    sep = ", ";
-                }
-                p += snprintf(p, end - p, ")");
-            }
+            if (t->narg)
+                p += fmtlist(p, end - p, t->arg, t->narg);
             break;
         case Tyname:
             if (t->name->name.ns)
                 p += snprintf(p, end - p, "%s.", t->name->name.ns);
             p += snprintf(p, end - p, "%s", namestr(t->name));
-            if (t->narg) {
-                arg = t->arg;
-                narg = t->narg;
-            } else {
-                arg = t->param;
-                narg = t->nparam;
-            }
-            if (!narg)
-                break;
-            p += snprintf(p, end - p, "(");
-            for (i = 0; i < narg; i++)  {
-                p += snprintf(p, end - p, "%s", sep);
-                p += tybfmt(p, end - p, arg[i]);
-                sep = ", ";
-            }
-            p += snprintf(p, end - p, ")");
+            if (t->narg)
+                p += fmtlist(p, end - p, t->arg, t->narg);
             break;
         case Tygeneric:
+            if (t->name->name.ns)
+                p += snprintf(p, end - p, "%s.", t->name->name.ns);
+            p += snprintf(p, end - p, "%s", namestr(t->name));
+            if (t->ngparam)
+                p += fmtlist(p, end - p, t->gparam, t->ngparam);
             break;
         case Tystruct:  p += fmtstruct(p, end - p, t);  break;
         case Tyunion:   p += fmtunion(p, end - p, t);   break;
@@ -825,9 +826,9 @@
             if (ty->arg)
                 for (i = 0; i < ty->narg; i++)
                     p += tyidfmt(p, end - p, ty->arg[i]);
-            else if (ty->param)
-                for (i = 0; i < ty->nparam; i++)
-                    p += tyidfmt(p, end - p, ty->param[i]);
+            else if (ty->gparam)
+                for (i = 0; i < ty->ngparam; i++)
+                    p += tyidfmt(p, end - p, ty->gparam[i]);
             break;
         case Tygeneric:
             break;
--- a/parse/use.c
+++ b/parse/use.c
@@ -253,17 +253,19 @@
         case Tyname:
             pickle(fd, ty->name);
             wrbool(fd, ty->issynth);
-
-            wrint(fd, ty->nparam);
-            for (i = 0; i < ty->nparam; i++)
-                wrtype(fd, ty->param[i]);
-
             wrint(fd, ty->narg);
             for (i = 0; i < ty->narg; i++)
                 wrtype(fd, ty->arg[i]);
-
             wrtype(fd, ty->sub[0]);
             break;
+        case Tygeneric:
+            pickle(fd, ty->name);
+            wrbool(fd, ty->issynth);
+            wrint(fd, ty->ngparam);
+            for (i = 0; i < ty->ngparam; i++)
+                wrtype(fd, ty->gparam[i]);
+            wrtype(fd, ty->sub[0]);
+            break;
         default:
             for (i = 0; i < ty->nsub; i++)
                 wrtype(fd, ty->sub[i]);
@@ -376,19 +378,21 @@
         case Tyname:
             ty->name = unpickle(fd);
             ty->issynth = rdbool(fd);
-
-            ty->nparam = rdint(fd);
-            ty->param = zalloc(ty->nparam * sizeof(Type *));
-            for (i = 0; i < ty->nparam; i++)
-                rdtype(fd, &ty->param[i]);
-
             ty->narg = rdint(fd);
             ty->arg = zalloc(ty->narg * sizeof(Type *));
             for (i = 0; i < ty->narg; i++)
                 rdtype(fd, &ty->arg[i]);
-
             rdtype(fd, &ty->sub[0]);
             break;
+        case Tygeneric:
+            ty->name = unpickle(fd);
+            ty->issynth = rdbool(fd);
+            ty->ngparam = rdint(fd);
+            ty->gparam = zalloc(ty->ngparam * sizeof(Type *));
+            for (i = 0; i < ty->ngparam; i++)
+                rdtype(fd, &ty->gparam[i]);
+            rdtype(fd, &ty->sub[0]);
+            break;
         default:
             for (i = 0; i < ty->nsub; i++)
                 rdtype(fd, &ty->sub[i]);
@@ -730,22 +734,17 @@
         t = htget(tidmap, itop(typefixid[i]));
         if (!t)
             die("Unable to find type for id %zd\n", typefixid[i]);
-        if (t->type == Tyname && !t->issynth) {
-            old = htget(tydedup, t->name);
-            if (old != t)
-            if (t != old)
-                t = old;
+        if ((t->type == Tyname || t->type == Tygeneric) && !t->issynth) {
+            t = htget(tydedup, t);
         }
         *typefixdest[i] = t;
-        if (!*typefixdest[i])
-            die("Couldn't find type %zd\n", typefixid[i]);
     }
     /* check for duplicate type definitions */
     for (i = 0; i < ntypefixdest; i++) {
         t = htget(tidmap, itop(typefixid[i]));
-        if (t->type != Tyname || t->issynth)
+        if ((t->type != Tyname && t->type != Tygeneric) || t->issynth)
             continue;
-        old = htget(tydedup, t->name);
+        old = htget(tydedup, t);
         if (old && !tyeq(t, old))
             lfatal(t->loc, "Duplicate definition of type %s on %s:%d", tystr(old), file->file.files[old->loc.file], old->loc.line);
     }
@@ -778,6 +777,23 @@
     lfree(&traitfixid, &ntraitfixid);
 }
 
+ulong tdhash(void *pt)
+{
+    Type *t;
+
+    t = pt;
+    return t->type * namehash(t->name);
+}
+
+int tdeq(void *pa, void *pb)
+{
+    Type *a, *b;
+
+    a = pa;
+    b = pb;
+    return a->type == b->type && nameeq(a->name, b->name);
+}
+
 /* Usefile format:
  *     U<pkgname>
  *     T<pickled-type>
@@ -800,7 +816,7 @@
 
     pushstab(file->file.globls);
     if (!tydedup)
-        tydedup = mkht(namehash, nameeq);
+        tydedup = mkht(tdhash, tdeq);
     if (fgetc(f) != 'U')
         return 0;
     pkg = rdstr(f);
@@ -863,13 +879,13 @@
                     ty->vis = vis;
                 htput(tidmap, itop(tid), ty);
                 /* fix up types */
-                if (ty->type == Tyname) {
+                if (ty->type == Tyname || ty->type == Tygeneric) {
                     if (ty->issynth)
                         break;
                     if (!gettype(s, ty->name) && !ty->ishidden)
                         puttype(s, ty->name, ty);
-                    if (!hthas(tydedup, ty->name))
-                        htput(tydedup, ty->name, ty);
+                    if (!hthas(tydedup, ty))
+                        htput(tydedup, ty, ty);
                 } else if (ty->type == Tyunion)  {
                     for (i = 0; i < ty->nmemb; i++)
                         if (!getucon(s, ty->udecls[i]->name) && !ty->udecls[i]->synth)
--- a/test/genericrec.myr
+++ b/test/genericrec.myr
@@ -8,9 +8,11 @@
 
 const main = {
 	var v : list(int)
-	var n
 
 	v.val = 123
 	v.next = std.alloc()
 	v.next.val = 234
+	var n = std.alloc()
+	v.next.next = n
+	n.val = 345
 }