Re: [RFC PATCH 2/2] Specialize impl declarations on impl type in addition to decl type
[Thread Prev] | [Thread Next]
- Subject: Re: [RFC PATCH 2/2] Specialize impl declarations on impl type in addition to decl type
- From: Ori Bernstein <ori@xxxxxxxxxxxxxx>
- Date: Tue, 4 Jul 2017 10:44:17 -0700
- To: Ori Bernstein <ori@xxxxxxxxxxxxxx>
- Cc: Michael Forney <mforney@xxxxxxxxxxx>, myrddin-dev@xxxxxxxxxxxxxx
Pushed it as is, since it doesn't introduce a regression -- whoever gets to it
first, though, should follow up and fix this.
On Sun, 2 Jul 2017 21:55:56 -0700, Ori Bernstein <ori@xxxxxxxxxxxxxx> wrote:
> Overall, the code looks pretty good.
>
> There's only one minor issue I've been able to find so far. It seems like
> this code prematurely rejects a trait specialized over a generic:
>
> use std
>
> trait foo @a =
> X : int
> ;;
>
> impl foo @a[:] =
> X = 123
> ;;
>
> impl foo byte[:] =
> X = 234
> ;;
>
>
> const main = {
> std.put("{}\n", impl(X, int[:]))
> //std.put("{}\n", impl(X, byte[:]))
> }
>
> This is pretty minor, since it only seems to break the new use of ->param,
> where there's no type to specialize over. I haven't dug in too deep to debug
> yet, but it's clear that it's trying to decide which trait is associated
> before doing the substitutions.
>
> The way that traits were initially designed to work is that impls didn't
> actually participate in type inference at all; The generics would get
> specialized with appropriate traits, and only after inference was complete
> would any checks be done.
>
> I haven't dug in too deeply, but I think bestimpl in specialize.c needs to
> know about the param in order to fix this.
>
> On Sat, 1 Jul 2017 14:43:22 -0700, Michael Forney <mforney@xxxxxxxxxxx> wrote:
>
> > 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
> >
> >
>
>
> --
> Ori Bernstein
>
--
Ori Bernstein
| [RFC PATCH 0/2] impl specialization and lookup changes | Michael Forney <mforney@xxxxxxxxxxx> |
| [RFC PATCH 2/2] Specialize impl declarations on impl type in addition to decl type | Michael Forney <mforney@xxxxxxxxxxx> |
| Re: [RFC PATCH 2/2] Specialize impl declarations on impl type in addition to decl type | Ori Bernstein <ori@xxxxxxxxxxxxxx> |
- Prev by Date: Re: [RFC PATCH 1/2] Add impl(type, name) to retrieve a particular implementation of a trait decl
- Next by Date: [PATCH] Allow matching of empty structs and arrays
- Previous by thread: Re: [RFC PATCH 2/2] Specialize impl declarations on impl type in addition to decl type
- Next by thread: Re: [RFC PATCH 0/2] impl specialization and lookup changes
- Index(es):