Eigenstate: myrddin-dev mailing list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC PATCH 2/2] Specialize impl declarations on impl type in addition to decl type


This allows multiple specializations of a declarations with a concrete type,
which can be selected with the new impl expression if it can't be deduced by its
type alone.

For example

	trait hasname @t =
		Name: byte[:]
	;;
	impl hasname void =
		Name = "somename"
	;;
	impl hasname bool =
		Name = "othername"
	;;
	const boolname = impl(Name, void)

To do this, pass the param type through to genericname and specializedcl. Since
we now need the type parameter to look up trait decls, make sure n->expr.param
gets the necessary treatment in typesub, specializenode, pickle, and unpickle.

We also need to tag the param types for export.
---
 doc/lang.txt               |  4 +++-
 mbld/deps.myr              |  2 +-
 parse/export.c             |  2 ++
 parse/infer.c              | 10 ++++++----
 parse/parse.h              |  6 +++---
 parse/specialize.c         | 16 +++++++++++-----
 parse/use.c                |  5 +++++
 test/implexpr-concrete.myr | 15 +++++++++++++++
 test/tests                 |  1 +
 9 files changed, 47 insertions(+), 14 deletions(-)
 create mode 100644 test/implexpr-concrete.myr

diff --git a/doc/lang.txt b/doc/lang.txt
index 3be5500f..d71bb70c 100644
--- a/doc/lang.txt
+++ b/doc/lang.txt
@@ -1268,7 +1268,9 @@ TABLE OF CONTENTS:
 
             An impl expression chooses the implementation of the given trait
             declaration for the given type. It is useful for refering to trait
-            declarations in a generic context.
+            declarations in a generic context. It also allows you to
+            disambiguate a trait declaration whose type does not refer to the
+            trait parameter.
 
         5.2.4. Cast Expressions:
 
diff --git a/mbld/deps.myr b/mbld/deps.myr
index 1bdcae7f..506fffa3 100644
--- a/mbld/deps.myr
+++ b/mbld/deps.myr
@@ -11,7 +11,7 @@ pkg bld =
 	const myrdeps	: (b : build#, mt : myrtarg#, doclean : bool, addsrc : bool -> depgraph#)
 ;;
 
-const Abiversion = 12
+const Abiversion = 13
 
 var usepat	: regex.regex#
 var cflagpat	: regex.regex#
diff --git a/parse/export.c b/parse/export.c
index 0c9be87d..4f67019f 100644
--- a/parse/export.c
+++ b/parse/export.c
@@ -150,6 +150,8 @@ static void tagnode(Stab *st, Node *n, int ingeneric, int hidelocal)
 	case Nexpr:
 		tagnode(st, n->expr.idx, ingeneric, hidelocal);
 		tagtype(st, n->expr.type, ingeneric, hidelocal);
+		if (n->expr.param)
+			tagtype(st, n->expr.param, ingeneric, hidelocal);
 		for (i = 0; i < n->expr.nargs; i++)
 			tagnode(st, n->expr.args[i], ingeneric, hidelocal);
 		/* generics need to have the decls they refer to exported. */
diff --git a/parse/infer.c b/parse/infer.c
index 445b34b4..100ea83e 100644
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -1814,7 +1814,7 @@ static void specializeimpl(Inferstate *st, Node *n)
 		unify(st, n, type(st, dcl), ty);
 
 		/* and put the specialization into the global stab */
-		name = genericname(proto, ty);
+		name = genericname(proto, n->impl.type, ty);
 		sym = getdcl(file->file.globls, name);
 		if (sym)
 			fatal(n, "trait %s already specialized with %s on %s:%d",
@@ -2423,6 +2423,8 @@ static void typesub(Inferstate *st, Node *n, int noerr)
 		break;
 	case Nexpr:
 		settype(st, n, tyfix(st, n, type(st, n), 0));
+		if (n->expr.param)
+			n->expr.param = tyfix(st, n, n->expr.param, 0);
 		typesub(st, n->expr.idx, noerr);
 		if (exprop(n) == Ocast && exprop(n->expr.args[0]) == Olit &&
 				n->expr.args[0]->expr.args[0]->lit.littype == Lint) {
@@ -2493,7 +2495,7 @@ static void specialize(Inferstate *st, Node *f)
 		pushstab(st->specializationscope[i]);
 		n = st->specializations[i];
 		if (n->type == Nexpr) {
-			d = specializedcl(st->genericdecls[i], n->expr.type, &name);
+			d = specializedcl(st->genericdecls[i], n->expr.param, n->expr.type, &name);
 			n->expr.args[0] = name;
 			n->expr.did = d->decl.did;
 
@@ -2506,11 +2508,11 @@ static void specialize(Inferstate *st, Node *f)
 			ty = exprtype(n->iterstmt.seq);
 
 			it = itertype(st, n->iterstmt.seq, mktype(n->loc, Tybool));
-			d = specializedcl(tr->proto[0], it, &name);
+			d = specializedcl(tr->proto[0], ty, it, &name);
 			htput(tr->proto[0]->decl.impls, ty, d);
 
 			it = itertype(st, n->iterstmt.seq, mktype(n->loc, Tyvoid));
-			d = specializedcl(tr->proto[1], it, &name);
+			d = specializedcl(tr->proto[1], ty, it, &name);
 			htput(tr->proto[1]->decl.impls, ty, d);
 		} else {
 			die("unknown node for specialization\n");
diff --git a/parse/parse.h b/parse/parse.h
index 6faeba2b..b0d56289 100644
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -1,4 +1,4 @@
-#define Abiversion 12
+#define Abiversion 13
 
 typedef struct Srcloc Srcloc;
 typedef struct Tysubst Tysubst;
@@ -508,9 +508,9 @@ void substput(Tysubst *subst, Type *from, Type *to);
 Type *substget(Tysubst *subst, Type *from);
 void substpush(Tysubst *subst);
 void substpop(Tysubst *subst);
-Node *specializedcl(Node *n, Type *to, Node **name);
+Node *specializedcl(Node *n, Type *param, Type *to, Node **name);
 Type *tyspecialize(Type *t, Tysubst *tymap, Htab *delayed, Htab *tybase);
-Node *genericname(Node *n, Type *t);
+Node *genericname(Node *n, Type *param, Type *t);
 void geninit(Node *file);
 
 /* usefiles */
diff --git a/parse/specialize.c b/parse/specialize.c
index e9805872..dbc2d07c 100644
--- a/parse/specialize.c
+++ b/parse/specialize.c
@@ -241,7 +241,7 @@ static void fixup(Node *n)
 			if (!d)
 				die("Missing decl %s", namestr(n->expr.args[0]));
 			if (d->decl.isgeneric)
-				d = specializedcl(d, n->expr.type, &n->expr.args[0]);
+				d = specializedcl(d, n->expr.param, n->expr.type, &n->expr.args[0]);
 			n->expr.did = d->decl.did;
 		}
 		break;
@@ -328,6 +328,8 @@ static Node *specializenode(Node *n, Tysubst *tsmap)
 	case Nexpr:
 		r->expr.op = n->expr.op;
 		r->expr.type = tysubst(n->expr.type, tsmap);
+		if (n->expr.param)
+			r->expr.param = tysubst(n->expr.param, tsmap);
 		r->expr.isconst = n->expr.isconst;
 		r->expr.nargs = n->expr.nargs;
 		r->expr.idx = specializenode(n->expr.idx, tsmap);
@@ -435,7 +437,7 @@ static Node *specializenode(Node *n, Tysubst *tsmap)
 	return r;
 }
 
-Node *genericname(Node *n, Type *t)
+Node *genericname(Node *n, Type *param, Type *t)
 {
 	char buf[1024];
 	char *p;
@@ -447,6 +449,10 @@ Node *genericname(Node *n, Type *t)
 	p = buf;
 	end = buf + sizeof buf;
 	p += bprintf(p, end - p, "%s", n->decl.name->name.name);
+	if (param) {
+		p += bprintf(p, end - p, "$");
+		p += tyidfmt(p, end - p, param);
+	}
 	p += bprintf(p, end - p, "$");
 	p += tyidfmt(p, end - p, t);
 	name = mkname(n->loc, buf);
@@ -574,7 +580,7 @@ Node *bestimpl(Node *n, Type *to)
  * duplicate of it with type 'to'. It also generates
  * a name for this specialized node, and returns it in '*name'.
  */
-Node *specializedcl(Node *gnode, Type *to, Node **name)
+Node *specializedcl(Node *gnode, Type *param, Type *to, Node **name)
 {
 	extern int stabstkoff;
 	Tysubst *tsmap;
@@ -584,7 +590,7 @@ Node *specializedcl(Node *gnode, Type *to, Node **name)
 	assert(gnode->type == Ndecl);
 	assert(gnode->decl.isgeneric);
 
-	n = genericname(gnode, to);
+	n = genericname(gnode, param, to);
 	*name = n;
 	if (n->name.ns)
 		st = getns(file, n->name.ns);
@@ -598,7 +604,7 @@ Node *specializedcl(Node *gnode, Type *to, Node **name)
 	if (gnode->decl.trait) {
 		g = bestimpl(gnode, to);
 		if (!g)
-			fatal(gnode, "no trait implemented for %s:%s", namestr(gnode->decl.name), tystr(to));
+			fatal(gnode, "type %s does not implement %s:%s", tystr(param), namestr(gnode->decl.name), tystr(to));
 	} else {
 		g = gnode;
 	}
diff --git a/parse/use.c b/parse/use.c
index 1c02a6b7..013a176d 100644
--- a/parse/use.c
+++ b/parse/use.c
@@ -464,6 +464,9 @@ static void pickle(FILE *fd, Node *n)
 	case Nexpr:
 		wrbyte(fd, n->expr.op);
 		wrtype(fd, n->expr.type);
+		wrbool(fd, n->expr.param != NULL);
+		if (n->expr.param)
+			wrtype(fd, n->expr.param);
 		wrbool(fd, n->expr.isconst);
 		pickle(fd, n->expr.idx);
 		wrint(fd, n->expr.nargs);
@@ -598,6 +601,8 @@ static Node *unpickle(FILE *fd)
 	case Nexpr:
 		n->expr.op = rdbyte(fd);
 		rdtype(fd, &n->expr.type);
+		if (rdbool(fd))
+			rdtype(fd, &n->expr.param);
 		n->expr.isconst = rdbool(fd);
 		n->expr.idx = unpickle(fd);
 		n->expr.nargs = rdint(fd);
diff --git a/test/implexpr-concrete.myr b/test/implexpr-concrete.myr
new file mode 100644
index 00000000..9d5eeb18
--- /dev/null
+++ b/test/implexpr-concrete.myr
@@ -0,0 +1,15 @@
+use std
+
+trait name @a =
+	Name: byte[:]
+;;
+impl name void =
+	Name = "zig"
+;;
+impl name int =
+	Name = "zag"
+;;
+
+const main = {
+	std.put("{}{}\n", impl(Name, void), impl(Name, int))
+}
diff --git a/test/tests b/test/tests
index 937828be..b7ab8e1d 100644
--- a/test/tests
+++ b/test/tests
@@ -165,3 +165,4 @@ B nestedgoto	E	0
 B initializer	E	0
 B fmtalign	E	0
 B implexpr	P	12,z,hello
+B implexpr-concrete	P	zigzag
-- 
2.13.2


Follow-Ups:
Re: [RFC PATCH 2/2] Specialize impl declarations on impl type in addition to decl typeOri Bernstein <ori@xxxxxxxxxxxxxx>
References:
[RFC PATCH 0/2] impl specialization and lookup changesMichael Forney <mforney@xxxxxxxxxxx>