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
}