shithub: mc

Download patch

ref: 128470e404d843b3d71c5837cac05d4d4203bd66
parent: fb8753c942a8d06926977d221d5c89c3a867fa29
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Jan 31 17:18:17 EST 2016

Add support for generic impls.

	You can now implement generic shit like iterators.

--- a/6/simp.c
+++ b/6/simp.c
@@ -507,7 +507,7 @@
 		name = declname(tr->funcs[i]);
 		if (!strcmp(fn, name)) {
 			proto = tr->funcs[i];
-			dcl = htget(proto->decl.impls, ty);
+			dcl = htget(proto->decl.__impls, ty);
 			var = mkexpr(loc, Ovar, dcl->decl.name, NULL);
 			var->expr.type = codetype(dcl->decl.type);
 			var->expr.did = dcl->decl.did;
--- a/parse/gram.y
+++ b/parse/gram.y
@@ -399,7 +399,7 @@
 			0);
 		for (i = 0; i < $6.nn; i++) {
 			$6.nl[i]->decl.trait = $$;
-			$6.nl[i]->decl.impls = mkht(tyhash, tyeq); 
+			$6.nl[i]->decl.__impls = mkht(tyhash, tyeq); 
 			$6.nl[i]->decl.isgeneric = 1;
 		}
 	}
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -809,8 +809,6 @@
 	return 2;
 }
 
-static int hasparam(Type *t) { return t->type == Tyname && t->narg > 0; }
-
 static void unionunify(Inferstate *st, Node *ctx, Type *u, Type *v)
 {
 	size_t i, j;
@@ -914,6 +912,10 @@
 				tystr(a), tystr(b), ctxstr(st, ctx));
 }
 
+static int hasargs(Type *t)
+{
+	return t->type == Tyname && t->narg > 0;
+}
 
 /* Unifies two types, or errors if the types are not unifiable. */
 static Type *unify(Inferstate *st, Node *ctx, Type *u, Type *v)
@@ -972,7 +974,7 @@
 	/* if the tyrank of a is 0 (ie, a raw tyvar), just unify.
 	 * Otherwise, match up subtypes. */
 	if (a->type == b->type && a->type != Tyvar) {
-		if (hasparam(a) && hasparam(b)) {
+		if (hasargs(a) && hasargs(b)) {
 			/* Only Tygeneric and Tyname should be able to unify. And they
 			 * should have the same names for this to be true. */
 			if (!nameeq(a->name, b->name))
@@ -1684,9 +1686,6 @@
 					namestr(t->name), ctxstr(st, n));
 
 		/* infer and unify types */
-		if (n->impl.type->type == Tygeneric || n->impl.type->type == Typaram)
-			fatal(n, "trait specialization requires concrete type, got %s",
-					tystr(n->impl.type));
 		verifytraits(st, n, t->param, n->impl.type);
 		subst = mksubst();
 		substput(subst, t->param, n->impl.type);
@@ -1707,7 +1706,13 @@
 				fname(sym->loc), lnum(sym->loc));
 		dcl->decl.name = name;
 		putdcl(file->file.globls, dcl);
-		htput(proto->decl.impls, n->impl.type, dcl);
+		htput(proto->decl.__impls, n->impl.type, dcl);
+		dcl->decl.isconst = 1;
+		if (n->impl.type->type == Tygeneric || hasparams(n->impl.type)) {
+			dcl->decl.isgeneric = 1;
+			lappend(&proto->decl.gimpl, &proto->decl.ngimpl, dcl);
+			lappend(&proto->decl.gtype, &proto->decl.ngtype, ty);
+		}
 		if (debugopt['S'])
 			printf("specializing trait [%d]%s:%s => %s:%s\n", n->loc.line,
 					namestr(proto->decl.name), tystr(type(st, proto)), namestr(name),
@@ -2071,7 +2076,7 @@
 	ty = NULL;
 	dcl = NULL;
 	if (n->expr.param)
-		dcl = htget(proto->decl.impls, tf(st, n->expr.param));
+		dcl = htget(proto->decl.__impls, tf(st, n->expr.param));
 	if (dcl)
 		ty = dcl->decl.type;
 	if (!ty)
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -328,9 +328,13 @@
 			 impl.
 			*/
 			Trait *trait;
-			Htab *impls;
-			char vis;
+			Htab *__impls;
+			Node **gimpl; /* generic impls of this trait */
+			size_t ngimpl;
+			Node **gtype; /* generic impls of this trait */
+			size_t ngtype;
 
+			char vis;
 			/* flags */
 			char isglobl;
 			char isconst;
--- a/parse/specialize.c
+++ b/parse/specialize.c
@@ -317,7 +317,9 @@
 	r = mknode(n->loc, n->type);
 	switch (n->type) {
 	case Nfile:
-	case Nuse:	die("Node %s not allowed here\n", nodestr[n->type]);	break;
+	case Nuse:
+		die("Node %s not allowed here\n", nodestr[n->type]);
+		break;
 	case Nexpr:
 		r->expr.op = n->expr.op;
 		r->expr.type = tysubst(n->expr.type, tsmap);
@@ -447,6 +449,107 @@
 	return name;
 }
 
+/* this doesn't walk through named types, so it can't recurse infinitely. */
+int matchquality(Type *pat, Type *to)
+{
+	int match, q;
+	size_t i;
+	Ucon *puc, *tuc;
+
+	if (pat->type == Typaram)
+		return 0;
+	else if (pat->type != to->type)
+		return -1;
+
+	match = 0;
+	switch (pat->type) {
+	case Tystruct:
+		if (pat->nmemb != to->nmemb)
+			return -1;
+		for (i = 0; i < pat->nmemb; i++) {
+			if (!streq(declname(pat->sdecls[i]),declname( to->sdecls[i])))
+				return -1;
+			q = matchquality(decltype(pat->sdecls[i]), decltype(to->sdecls[i]));
+			if (q < 0)
+				return -1;
+			match += q;
+		}
+		break;
+	case Tyunion:
+		if (pat->nmemb != to->nmemb)
+			return -1;
+		for (i = 0; i < pat->nmemb; i++) {
+			if (!nameeq(pat->udecls[i], to->udecls[i]))
+				return -1;
+			puc = pat->udecls[i];
+			tuc = to->udecls[i];
+			if (puc->etype && tuc->etype) {
+				q = matchquality(puc->etype, tuc->etype);
+				if (q < 0)
+					return -1;
+				match += q;
+			} else if (puc->etype != tuc->etype) {
+				return -1;
+			}
+		}
+		break;
+	case Tyname:
+		if (!nameeq(pat->name, to->name))
+			return -1;
+		if (pat->narg != to->narg)
+			return -1;
+		for (i = 0; i < pat->narg; i++) {
+			q = matchquality(pat->sub[i], to->sub[i]);
+			if (q < 0)
+				return -1;
+			match += q;
+		}
+		break;
+	default:
+		if (pat->nsub != to->nsub)
+			break;
+		if (pat->type == to->type)
+			match = 1;
+		for (i = 0; i < pat->nsub; i++) {
+			q = matchquality(pat->sub[i], to->sub[i]);
+			if (q < 0)
+				return -1;
+			match += q;
+		}
+		break;
+	}
+	return match;
+}
+
+Node *bestimpl(Node *n, Type *to)
+{
+	Node **gimpl, **ambig, *match;
+	size_t ngimpl, nambig, i;
+	int score;
+	int best;
+
+	ambig = NULL;
+	nambig = 0;
+	best = -1;
+	gimpl = n->decl.gimpl;
+	ngimpl = n->decl.ngimpl;
+	for (i = 0; i < ngimpl; i++) {
+		score = matchquality(decltype(gimpl[i]), to);
+		if (score > best) {
+			lfree(&ambig, &nambig);
+			best = score;
+			match = gimpl[i];
+		} else if (score == best) {
+			lappend(&ambig, &nambig, gimpl[i]);
+		}
+	}
+	if (best < 0)
+		return NULL;
+	else if (nambig > 0)
+		fatal(n, "ambiguous implementation for %s", tystr(to));
+	return match;
+}
+
 /*
  * Takes a generic declaration, and creates a specialized
  * duplicate of it with type 'to'. It also generates
@@ -471,15 +574,16 @@
 	if (!st)
 		fatal(n, "Can't find symbol table for %s.%s", n->name.ns, n->name.name);
 	d = getdcl(st, n);
-	if (debugopt['S'])
-		printf("depth[%d] specializing [%d]%s => %s\n", stabstkoff, g->loc.line,
-			namestr(g->decl.name), namestr(n));
 	if (d)
 		return d;
 	if (g->decl.trait) {
-		printf("%s\n", namestr(n));
-		fatal(g, "no trait implemented for for %s:%s", namestr(g->decl.name), tystr(to));
+		g = bestimpl(g, to);
+		if (!g)
+			fatal(g, "no trait implemented for for %s:%s", namestr(g->decl.name), tystr(to));
 	}
+	if (debugopt['S'])
+		printf("depth[%d] specializing [%d]%s => %s\n", stabstkoff, g->loc.line,
+			namestr(g->decl.name), namestr(n));
 	/* namespaced names need to be looked up in their correct
 	 * context. */
 	if (n->name.ns)
--- a/parse/type.c
+++ b/parse/type.c
@@ -856,7 +856,7 @@
 
 	func = mkdecl(Zloc, mkname(Zloc, "__iternext__"), ty);
 	func->decl.trait = tr;
-	func->decl.impls = mkht(tyhash, tyeq); 
+	func->decl.__impls = mkht(tyhash, tyeq); 
 	func->decl.isgeneric = 1;
 	func->decl.isconst = 1;
 	func->decl.isglobl = 1;
@@ -876,7 +876,7 @@
 
 	func = mkdecl(Zloc, mkname(Zloc, "__iterfin__"), ty);
 	func->decl.trait = tr;
-	func->decl.impls = mkht(tyhash, tyeq); 
+	func->decl.__impls = mkht(tyhash, tyeq); 
 	func->decl.isgeneric = 1;
 	func->decl.isconst = 1;
 	func->decl.isglobl = 1;
--- a/parse/use.c
+++ b/parse/use.c
@@ -813,14 +813,20 @@
 {
 	size_t i, len;
 	char *protoname, *dclname, *p;
+	Node *proto;
 
 	dclname = declname(dcl);
 	for (i = 0; i < tr->nfuncs; i++) {
-		protoname = declname(tr->funcs[i]);
+		proto = tr->funcs[i];
+		protoname = declname(proto);
 		len = strlen(protoname);
 		p = strstr(dclname, protoname);
 		if (p && p[len] == '$')
-			htput(tr->funcs[i]->decl.impls, ty, dcl);
+			htput(proto->decl.__impls, ty, dcl);
+		if (ty->type == Tygeneric || hasparams(ty)) {
+			lappend(&proto->decl.gimpl, &proto->decl.ngimpl, dcl);
+			lappend(&proto->decl.gtype, &proto->decl.ngtype, ty);
+		}
 	}
 }
 
@@ -948,7 +954,7 @@
 			  puttrait(s, tr->name, tr);
 			  for (i = 0; i < tr->nfuncs; i++) {
 				  putdcl(s, tr->funcs[i]);
-				  tr->funcs[i]->decl.impls = mkht(tyhash, tyeq);
+				  tr->funcs[i]->decl.__impls = mkht(tyhash, tyeq);
 			  }
 			  break;
 		case 'T':
--- /dev/null
+++ b/test/genericimpl.myr
@@ -1,0 +1,33 @@
+use std
+
+trait gimpl @a =
+	desc	: (x : @a -> byte[:])
+;;
+
+impl gimpl int =
+	desc = {x
+		-> "int"
+	}
+;;
+
+impl gimpl byte[:] =
+	desc = {x
+		-> "string"
+	}
+;;
+
+impl gimpl @a =
+	desc = {x
+		-> "@a"
+	}
+;;
+
+impl gimpl @b[:] =
+	desc = {x
+		-> "@a[:]"
+	}
+;;
+
+const main = {
+	std.put("{} {} {} {}\n", desc(123), desc("foo"), desc('x'), desc([true][:]))
+}
--- a/test/tests
+++ b/test/tests
@@ -93,6 +93,7 @@
 B genericret	E	42
 B genericmatch	E	15
 B genericrec	E	0
+B genericimpl	P	'int string @a @a[:]'
 B recgeneric	P	'built'
 B bigtyblob	P	'`U100 '
 # B genericchain	P	'val = 123' ## BUGGERED