shithub: mc

Download patch

ref: b5cf804cbeb33a60bea00307c24aa361ea821530
parent: 6e064ade4c10bce0127ebebf6cd1dc2bb24213f5
author: Ori Bernstein <ori@eigenstate.org>
date: Wed Jul 5 18:34:12 EDT 2017

Add some discipline to type bindings.

	We actually now do it in terms of scopes. It's still hacky,
	but...

--- a/parse/gram.y
+++ b/parse/gram.y
@@ -753,8 +753,9 @@
 		$$ = mkexpr($1->loc, Ocast, $2, NULL);
 		$$->expr.type = $4;
 	}
-	| Tsizeof Toparen type Tcparen
-	{$$ = mkexpr($1->loc, Osize, mkpseudodecl($1->loc, $3), NULL);}
+	| Tsizeof Toparen type Tcparen {
+		$$ = mkexpr($1->loc, Osize, mkpseudodecl($1->loc, $3), NULL);
+	}
 	| Timpl Toparen name Tcomma type Tcparen {
 		$$ = mkexpr($1->loc, Ovar, $3, NULL);
 		$$->expr.param = $5;
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -20,7 +20,6 @@
 	/* tracking where we are in the inference */
 	int ingeneric;
 	int inaggr;
-	int innamed;
 	int indentdepth;
 	Type *ret;
 	Srcloc *usrc;
@@ -58,8 +57,8 @@
 static void tybind(Inferstate *st, Type *t);
 static Type *tyfix(Inferstate *st, Node *ctx, Type *orig, int noerr);
 static void bind(Inferstate *st, Node *n);
-static void tyunbind(Inferstate *st, Type *t);
-static void unbind(Inferstate *st, Node *n);
+static void tyunbind(Inferstate *st);
+static void unbind(Inferstate *st);
 static Type *unify(Inferstate *st, Node *ctx, Type *a, Type *b);
 static Type *tf(Inferstate *st, Type *t);
 
@@ -418,8 +417,6 @@
 /* Freshens the type of a declaration. */
 static Type *tyfreshen(Inferstate *st, Tysubst *subst, Type *t)
 {
-	char *from, *to;
-
 	if (!needfreshen(st, t)) {
 		if (debugopt['u'])
 			indentf(st->indentdepth, "%s isn't generic: skipping freshen\n", tystr(t));
@@ -426,7 +423,6 @@
 		return t;
 	}
 
-	from = tystr(t);
 	tybind(st, t);
 	if (!subst) {
 		subst = mksubst();
@@ -435,14 +431,7 @@
 	} else {
 		t = tyspecialize(t, subst, st->delayed, st->seqbase);
 	}
-	tyunbind(st, t);
-	if (debugopt['u']) {
-		to = tystr(t);
-		indentf(st->indentdepth, "Freshen %s => %s\n", from, to);
-		free(to);
-	}
-	free(from);
-
+	tyunbind(st);
 	return t;
 }
 
@@ -454,6 +443,7 @@
 
 	if (t->resolved)
 		return;
+
 	/* type resolution should never throw errors about non-generics
 	 * showing up within a generic type, so we push and pop a generic
 	 * around resolution */
@@ -460,12 +450,14 @@
 	st->ingeneric++;
 	t->resolved = 1;
 	/* Walk through aggregate type members */
-	if (t->type == Tystruct) {
+	switch (t->type) {
+	case Tystruct:
 		st->inaggr++;
 		for (i = 0; i < t->nmemb; i++)
 			infernode(st, &t->sdecls[i], NULL, NULL);
 		st->inaggr--;
-	} else if (t->type == Tyunion) {
+		break;
+	case Tyunion:
 		st->inaggr++;
 		for (i = 0; i < t->nmemb; i++) {
 			t->udecls[i]->utype = t;
@@ -476,20 +468,25 @@
 			}
 		}
 		st->inaggr--;
-	} else if (t->type == Tyarray) {
+		break;
+	case Tyarray:
 		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) {
+		break;
+	case Typaram:
 		if (!isbound(st, t))
-			lfatal(
-					t->loc, "type parameter %s is undefined in generic context", tystr(t));
-	}
-
-	if (t->type == Tyname || t->type == Tygeneric) {
+			lfatal(t->loc, "type parameter %s is undefined in generic context", tystr(t));
+		break;
+	case Tyname:
+	case Tygeneric:
+		/* FIXME: this should not include the current type scope in the search. */
 		tybind(st, t);
-		st->innamed++;
+		break;
+	default:
+		break;
 	}
+
 	for (i = 0; i < t->nsub; i++)
 		t->sub[i] = tf(st, t->sub[i]);
 	base = tybase(t);
@@ -500,15 +497,15 @@
 		t->traits = bsdup(base->traits);
 	if (occurs(st, t))
 		lfatal(t->loc, "type %s includes itself", tystr(t));
+	if (t->type == Tygeneric || t->type == Tyname)
+		tyunbind(st);
 	st->ingeneric--;
-	if (t->type == Tyname || t->type == Tygeneric) {
-		tyunbind(st, t);
-		st->innamed--;
-	}
 }
 
 Type *tysearch(Type *t)
 {
+	if (!t)
+		return t;
 	while (tytab[t->tid])
 		t = tytab[t->tid];
 	return t;
@@ -587,7 +584,9 @@
 			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);
 		}
+		tybind(st, t);
 		t = tysubst(st, t, orig);
+		tyunbind(st);
 	}
 	st->ingeneric -= isgeneric;
 	return t;
@@ -693,6 +692,7 @@
 {
 	size_t i;
 
+	t = tysearch(t);
 	if (bshas(visited, t->tid))
 		return;
 	bsput(visited, t->tid);
@@ -744,21 +744,30 @@
 	bsfree(visited);
 }
 
-static void tybind(Inferstate *st, Type *t)
+static void tybindall(Inferstate *st, Type *t)
 {
 	Htab *bt;
-	char *s;
 
-	if (debugopt['u']) {
-		s = tystr(t);
-		indentf(st->indentdepth, "Binding %s\n", s);
-		free(s);
-	}
 	bt = mkht(strhash, streq);
 	lappend(&st->tybindings, &st->ntybindings, bt);
 	putbindings(st, bt, t);
 }
 
+static void tybind(Inferstate *st, Type *t)
+{
+	Htab *bt;
+	size_t i;
+
+	bt = mkht(strhash, streq);
+	lappend(&st->tybindings, &st->ntybindings, bt);
+	if (t->type == Tygeneric)
+		for (i = 0; i < t->ngparam; i++)
+			putbindings(st, bt, t->gparam[i]);
+	else if (t->type == Tyname)
+		for (i = 0; i < t->narg; i++)
+			putbindings(st, bt, t->arg[i]);
+}
+
 /* Binds the type parameters in the
  * declaration into the type environment */
 static void bind(Inferstate *st, Node *n)
@@ -766,10 +775,6 @@
 	Htab *bt;
 
 	assert(n->type == Ndecl);
-	if (!n->decl.isgeneric)
-		return;
-	if (!n->decl.init)
-		fatal(n, "generic %s has no initializer", n->decl);
 
 	st->ingeneric++;
 	bt = mkht(strhash, streq);
@@ -776,24 +781,21 @@
 	lappend(&st->tybindings, &st->ntybindings, bt);
 
 	putbindings(st, bt, n->decl.type);
-	putbindings(st, bt, n->decl.init->expr.type);
+	if (n->decl.init)
+		putbindings(st, bt, n->decl.init->expr.type);
 }
 
 /* Rolls back the binding of type parameters in
  * the type environment */
-static void unbind(Inferstate *st, Node *n)
+static void unbind(Inferstate *st)
 {
-	if (!n->decl.isgeneric)
-		return;
 	htfree(st->tybindings[st->ntybindings - 1]);
 	lpop(&st->tybindings, &st->ntybindings);
 	st->ingeneric--;
 }
 
-static void tyunbind(Inferstate *st, Type *t)
+static void tyunbind(Inferstate *st)
 {
-	if (t->type != Tygeneric)
-		return;
 	htfree(st->tybindings[st->ntybindings - 1]);
 	lpop(&st->tybindings, &st->ntybindings);
 }
@@ -1202,6 +1204,7 @@
 	param = n->expr.param;
 	if (s->decl.isgeneric) {
 		subst = mksubst();
+		tybindall(st, s->decl.type);
 		if (param)
 			substput(subst, s->decl.trait->param, param);
 		t = tysubstmap(st, subst, tf(st, s->decl.type), s->decl.type);
@@ -1210,6 +1213,7 @@
 			if (!param)
 				fatal(n, "ambiguous trait decl %s", ctxstr(st, s));
 		}
+		tyunbind(st);
 		substfree(subst);
 	} else {
 		t = s->decl.type;
@@ -1360,6 +1364,13 @@
 
 	*isconst = 1;
 	uc = uconresolve(st, n);
+	/* Hackety hack hack.
+	 * the types in a generic union may be bound from the tyname that
+	 * defined it, which is not accessible here.
+	 *
+	 * To make it compile, for now, we just bind the types in here.
+	 */
+	tybindall(st, uc->utype);
 	t = tysubst(st, tf(st, uc->utype), uc->utype);
 	uc = tybase(t)->udecls[uc->id];
 	if (uc->etype) {
@@ -1368,6 +1379,7 @@
 		*isconst = n->expr.args[1]->expr.isconst;
 	}
 	settype(st, n, delayeducon(st, t));
+	tyunbind(st);
 }
 
 static void inferpat(Inferstate *st, Node **np, Node *val, Node ***bind, size_t *nbind)
@@ -1680,7 +1692,10 @@
 		   infersub(st, n, ret, sawret, &isconst);
 		   switch (args[0]->lit.littype) {
 		   case Lfunc:
+			   tybindall(st, args[0]->lit.fnval->func.type);
 			   infernode(st, &args[0]->lit.fnval, NULL, NULL);
+			   tyunbind(st);
+
 			   /* FIXME: env capture means this is non-const */
 			   n->expr.isconst = 1;
 			   break;
@@ -1866,7 +1881,11 @@
 	k = htkeys(s->dcl, &n);
 	for (i = 0; i < n; i++) {
 		dcl = htget(s->dcl, k[i]);
+		if (dcl->decl.isgeneric)
+			bind(st, dcl);
 		tf(st, type(st, dcl));
+		if (dcl->decl.isgeneric)
+			unbind(st);
 	}
 	free(k);
 
@@ -1878,7 +1897,7 @@
 		t = tysearch(t);
 		tybind(st, t);
 		tyresolve(st, t);
-		tyunbind(st, t);
+		tyunbind(st);
 		updatetype(s, k[i], t);
 	}
 	free(k);
@@ -1908,12 +1927,14 @@
 		if (debugopt['u'])
 			indentf(st->indentdepth, "--- infer %s ---\n", declname(n));
 		st->indentdepth++;
-		bind(st, n);
+		if (n->decl.isgeneric)
+			bind(st, n);
 		inferdecl(st, n);
 		if (type(st, n)->type == Typaram && !st->ingeneric)
 			fatal(n, "generic type %s in non-generic near %s", tystr(type(st, n)),
 					ctxstr(st, n));
-		unbind(st, n);
+		if (n->decl.isgeneric)
+			unbind(st);
 		st->indentdepth--;
 		if (debugopt['u'])
 			indentf(st->indentdepth, "--- done ---\n");
@@ -2254,6 +2275,7 @@
 	size_t n, i;
 	Type *t;
 	Node *d;
+	char *dt;
 
 	k = htkeys(s->ty, &n);
 	for (i = 0; i < n; i++) {
@@ -2271,8 +2293,11 @@
 			continue;
 		if (d->decl.trait)
 			continue;
+		dt = "const";
+		if (d->decl.isgeneric)
+			dt = "generic";
 		if (!d->decl.isimport && !d->decl.isextern && !d->decl.init)
-			fatal(d, "non-extern constant \"%s\" has no initializer", ctxstr(st, d));
+			fatal(d, "non-extern %s \"%s\" has no initializer", ctxstr(st, d));
 	}
 	free(k);
 }
@@ -2377,6 +2402,8 @@
 		popstab();
 		break;
 	case Ndecl:
+		if(n->decl.isgeneric)
+			bind(st, n);
 		settype(st, n, tyfix(st, n, type(st, n), noerr));
 		if (n->decl.init)
 			typesub(st, n->decl.init, noerr);
@@ -2387,6 +2414,8 @@
 		if (streq(declname(n), "__init__"))
 			if (!initcompatible(tybase(decltype(n))))
 				fatal(n, "__init__ must be (->void), got %s", tystr(decltype(n)));
+		if (n->decl.isgeneric)
+			unbind(st);
 		break;
 	case Nblock:
 		pushstab(n->block.scope);
@@ -2448,7 +2477,7 @@
 		settype(st, n, tyfix(st, n, type(st, n), 0));
 		switch (n->lit.littype) {
 		case Lfunc:	typesub(st, n->lit.fnval, noerr);	break;
-		case Lint: checkrange(st, n);
+		case Lint:	checkrange(st, n);
 		default: break;
 		}
 		break;
@@ -2523,7 +2552,7 @@
 
 void applytraits(Inferstate *st, Node *f)
 {
-	size_t i;
+	size_t i, j;
 	Node *impl, *n;
 	Trait *tr;
 	Type *ty;
@@ -2549,11 +2578,17 @@
 				fatal(impl, "incompatible implementation of %s: mismatched aux types",
 						namestr(impl->impl.traitname), ctxstr(st, impl));
 		}
+		tybindall(st, impl->impl.type);
+		for (j = 0; j < impl->impl.naux; j++)
+			tybindall(st, impl->impl.aux[j]);
 		ty = tf(st, impl->impl.type);
 		settrait(ty, tr);
 		if (tr->uid == Tciter) {
 			htput(st->seqbase, tf(st, impl->impl.type), tf(st, impl->impl.aux[0]));
 		}
+		tyunbind(st);
+		for (j = 0; j < impl->impl.naux; j++)
+			tyunbind(st);
 	}
 	popstab();
 }
@@ -2595,6 +2630,7 @@
 	postcheck(&st);
 
 	/* and replace type vars with actual types */
+	assert(st.ntybindings == 0);
 	typesub(&st, file, 0);
 	specialize(&st, file);
 	verify(&st, file);
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -277,6 +277,7 @@
 			Node *name;
 			Type *type;
 			Node *init;
+
 			/*
 			 If we have a link to a trait, we should only look it up
 			 when specializing, but we should not create a new decl