ref: 1c6aa08fdbac936e06b36722b1180a39592792c4
parent: fcc859d58857fce2701ee30abd79ed891a46c3b2
author: Ori Bernstein <[email protected]>
date: Sun Nov 15 14:02:08 EST 2015
Give a good error on undefined generic parameters.
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -17,9 +17,10 @@
typedef struct Inferstate Inferstate;
struct Inferstate {
int ingeneric;
+ int inaggr;
+ int innamed;
int sawret;
int indentdepth;
- int intype;
Type *ret;
/* bound by patterns turn into decls in the action block */
@@ -260,16 +261,30 @@
static int isbound(Inferstate *st, Type *t)
{
ssize_t i;
- Type *p;
for (i = st->ntybindings - 1; i >= 0; i--) {
- p = htget(st->tybindings[i], t->pname);
- if (p == t)
+ if (htget(st->tybindings[i], t->pname))
return 1;
}
return 0;
}
+void dumpbound(Inferstate *st)
+{
+ Type *t;
+ void **k;
+ ssize_t i;
+ size_t nk, j;
+
+ for (i = st->ntybindings - 1; i >= 0; i--) {
+ k = htkeys(st->tybindings[i], &nk);
+ for (j = 0; j < nk; j++) {
+ t = htget(st->tybindings[i], k[j]);
+ printf("bound: %s\n", t->pname);
+ }
+ }
+}
+
/* Checks if a type that directly contains itself.
* Recursive types that contain themselves through
* pointers or slices are fine, but any other self-inclusion
@@ -389,7 +404,6 @@
return t;
}
-
/* Resolves a type and all it's subtypes recursively.*/
static void tyresolve(Inferstate *st, Type *t)
{
@@ -405,11 +419,12 @@
t->resolved = 1;
/* Walk through aggregate type members */
if (t->type == Tystruct) {
- st->intype++;
+ st->inaggr++;
for (i = 0; i < t->nmemb; i++)
infernode(st, &t->sdecls[i], NULL, NULL);
- st->intype--;
+ st->inaggr--;
} else if (t->type == Tyunion) {
+ st->inaggr++;
for (i = 0; i < t->nmemb; i++) {
t->udecls[i]->utype = t;
t->udecls[i]->utype = tf(st, t->udecls[i]->utype);
@@ -418,12 +433,20 @@
t->udecls[i]->etype = tf(st, t->udecls[i]->etype);
}
}
+ st->inaggr--;
} else if (t->type == Tyarray) {
- if (!st->intype && !t->asize)
+ if (!st->inaggr && !t->asize)
lfatal(t->loc, "unsized array type outside of struct");
infernode(st, &t->asize, NULL, NULL);
+ } else if (t->type == Typaram && st->innamed) {
+ if (!isbound(st, t))
+ lfatal(t->loc, "type parameter %s is undefined in generic context", tystr(t));
}
+ if (t->type == Tyname || t->type == Tygeneric) {
+ tybind(st, t);
+ st->innamed++;
+ }
for (i = 0; i < t->nsub; i++)
t->sub[i] = tf(st, t->sub[i]);
base = tybase(t);
@@ -435,6 +458,10 @@
if (tyinfinite(st, t, NULL))
lfatal(t->loc, "type %s includes itself", tystr(t));
st->ingeneric--;
+ if (t->type == Tyname || t->type == Tygeneric) {
+ tyunbind(st, t);
+ st->innamed--;
+ }
}
/* Look up the best type to date in the unification table, returning it */
@@ -602,31 +629,59 @@
return uc;
}
+static void putbindingsrec(Inferstate *st, Htab *bt, Type *t, Bitset *visited)
+{
+ size_t i;
+
+ if (bshas(visited, t->tid))
+ return;
+ bsput(visited, t->tid);
+ switch (t->type) {
+ case Typaram:
+ if (hthas(bt, t->pname))
+ unify(st, NULL, htget(bt, t->pname), t);
+ else if (!isbound(st, t))
+ htput(bt, t->pname, t);
+ break;
+ case Tygeneric:
+ for (i = 0; i < t->ngparam; i++)
+ putbindingsrec(st, bt, t->gparam[i], visited);
+ break;
+ case Tyname:
+ for (i = 0; i < t->narg; i++)
+ putbindingsrec(st, bt, t->arg[i], visited);
+ break;
+ case Tyunres:
+ for (i = 0; i < t->narg; i++)
+ putbindingsrec(st, bt, t->arg[i], visited);
+ break;
+ case Tystruct:
+ for (i = 0; i < t->nmemb; i++)
+ putbindingsrec(st, bt, t->sdecls[i]->decl.type, visited);
+ break;
+ case Tyunion:
+ for (i = 0; i < t->nmemb; i++)
+ if (t->udecls[i]->etype)
+ putbindingsrec(st, bt, t->udecls[i]->etype, visited);
+ break;
+ default:
+ for (i = 0; i < t->nsub; i++)
+ putbindingsrec(st, bt, t->sub[i], visited);
+ break;
+ }
+}
+
/* Binds the type parameters present in the
* current type into the type environment */
static void putbindings(Inferstate *st, Htab *bt, Type *t)
{
- size_t i;
- char *s;
+ Bitset *visited;
if (!t)
return;
- if (t->type != Typaram)
- return;
-
- if (debugopt['u']) {
- s = tystr(t);
- indentf(st->indentdepth, "Bind %s\n", s);
- free(s);
- }
- if (hthas(bt, t->pname))
- unify(st, NULL, htget(bt, t->pname), t);
- else if (isbound(st, t))
- return;
-
- htput(bt, t->pname, t);
- for (i = 0; i < t->narg; i++)
- putbindings(st, bt, t->arg[i]);
+ visited = mkbs();
+ putbindingsrec(st, bt, t, visited);
+ bsfree(visited);
}
static void tybind(Inferstate *st, Type *t)
@@ -634,8 +689,6 @@
Htab *bt;
char *s;
- if (t->type != Tygeneric)
- return;
if (debugopt['u']) {
s = tystr(t);
indentf(st->indentdepth, "Binding %s\n", s);
@@ -1610,6 +1663,9 @@
if (!t)
fatal(k[i], "undefined type %s", namestr(k[i]));
t = tysearch(t);
+ tybind(st, t);
+ tyresolve(st, t);
+ tyunbind(st, t);
updatetype(s, k[i], t);
}
free(k);
@@ -1618,7 +1674,7 @@
static void infernode(Inferstate *st, Node **np, Type *ret, int *sawret)
{
size_t i, nbound;
- Node **bound, *n;
+ Node **bound, *n, *pat;
Type *t;
n = *np;
@@ -1691,7 +1747,8 @@
fatal(n, "can't match against a void type near %s", ctxstr(st, n->matchstmt.val));
for (i = 0; i < n->matchstmt.nmatches; i++) {
infernode(st, &n->matchstmt.matches[i], ret, sawret);
- unify(st, n->matchstmt.matches[i]->match.pat, type(st, n->matchstmt.val), type(st, n->matchstmt.matches[i]->match.pat));
+ pat = n->matchstmt.matches[i]->match.pat;
+ unify(st, pat, type(st, n->matchstmt.val), type(st, n->matchstmt.matches[i]->match.pat));
}
break;
case Nmatch:
@@ -1760,10 +1817,10 @@
if (t->type == Tyarray) {
typesub(st, t->asize, noerr);
} else if (t->type == Tystruct) {
- st->intype++;
+ st->inaggr++;
for (i = 0; i < t->nmemb; i++)
typesub(st, t->sdecls[i], noerr);
- st->intype--;
+ st->inaggr--;
} else if (t->type == Tyunion) {
for (i = 0; i < t->nmemb; i++) {
if (t->udecls[i]->etype) {