September 24, 2022

Hello everyone!

My project wants to replace the usage of libdparse with dmd-as-a-library in D-Scanner, and improvind the dmd interface by using it in a real life scenario.

My intention is to attempt and template all the public methods in hdrgen.d. Since header generation is done right after parsing, we don’t need to import files related to semantic analysis, so the goal here would be to get rid of all the imports that refer files with information relevant for semantics, and at the same time it would be useful to be able to use hdrgen with any AST family, not just with ASTCodegen.

These are some of the imports present in hdrgen.d:

import dmd.declaration;
import dmd.denum;
import dmd.dimport;
import dmd.dmodule; // like this one for example
import dmd.doc;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.dversion;
import dmd.expression;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.init;

Example of how a templated function would look like:

void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs)
{
    scope v = new StatementPrettyPrintVisitor(buf, hgs);
    (cast() s).accept(v);
}

to:

void toCBuffer(AST = dmd.astcodegen.ASTCodegen)(const AST.Statement s, OutBuffer* buf, HdrGenState* hgs)
{
    scope v = new StatementPrettyPrintVisitor(buf, hgs);
    (cast() s).accept(v);
}

By templating that method it would bring great benefits to ASTBase, because toCBuffer is needed in order to print the content of an AST node. Example:
ASTCodegen(statement.d)

override final const(char)* toChars() const
    {
        HdrGenState hgs;
        OutBuffer buf;
        .toCBuffer(this, &buf, &hgs);
        buf.writeByte(0);
        return buf.extractSlice().ptr;
    }

This behavior can’t be implemented also in ASTBase currently, but it would be possible if the functions in hdrgen.d were templated.

Now regarding the example for the toCBuffer function, this also has the following downsides: Taking advantage of the default template parameter means that we still have to import ASTCodegen (and implicitly all files necessary for semantics), and this would also create a circular dependency:

hdrgen.d

import dmd.astcodegen

astcodegen.d

import dmd.hdrgen

A solution that avoids these problems would be not to use a default value for templated functions, but that implies a lost of find and replace work: Example

Statement.d

override final const(char)* toChars() const
    {
        HdrGenState hgs;
        OutBuffer buf;

       .toCBuffer!ASTCodegen(this, &buf, &hgs); // instead of old toCBuffer(...)
        buf.writeByte(0);
        return buf.extractSlice().ptr;
    }

Since this involves a good amount of work I would like to hear your thoughts before diving into the implementation.

September 25, 2022
On 25/09/2022 8:29 AM, Lucian Danescu wrote:
> My intention is to attempt and template all the public methods in `hdrgen.d`. Since header generation is done right after parsing, we don’t need to import files related to semantic analysis, so the goal here would be to get rid of all the imports that refer files with information relevant for semantics, and at the same time it would be useful to be able to use `hdrgen` with any AST family, not just with `ASTCodegen`.

This is outside the scope of your project but:

I made a joke poll on BeerConf that we should remove that second AST implementation rather than binary literals.

I'm mentioning it here as it is really quite absurd just how much extra complexity this one thing adds.