Bug 3952d: __ctfeWrite #692

Closed
wants to merge 3 commits into
from

Projects

None yet
@kennytm
Contributor
kennytm commented Feb 3, 2012

(This is equivalent to #296, except that I have rebased the changes on top of the master HEAD again. Please see #296 for more comments.)


Make DMD recognize the __ctfeWrite function at interpret-time, and print the arguments to console when used.

Note: dlang/druntime#155 need to be pulled as well.

For example, the code

bool x() {
  foreach (i; 0 .. 4)
    __ctfeWriteln(i, "^^2 == ", i*i);
  return true;
}
enum _ = x();

will print

0^^2 == 0
1^^2 == 1
2^^2 == 4
3^^2 == 9

when compiling.

This is not a replacement of pragma(msg).


(BTW, Phobos's writeln can still behave like #237 by adding a CTFE version:

void write(T...)(T x) {
      if (__ctfe)
        __ctfeWrite(x);
      else
        stdout.write(x);
}

)

@kennytm kennytm referenced this pull request Feb 3, 2012
Closed

Bug 3952d: __ctfeWriteln #296

@klickverbot
Member

I already commented on the first incarnation of the pull request, but since I think it is important, I'll do again again: Is it really wise to force appending a newline on every call? See #296 (comment).

@yebblies
Member
yebblies commented Feb 4, 2012

You don't need to open a new pull request when you rebase, you can just git push origin branch -f to update the branch, and the pull request.

@kennytm
Contributor
kennytm commented Feb 4, 2012

@yebblies I'm not sure if doing that will cause the comments in the request to disappear.

@klickverbot I think it's better to provide the writeln first, and if people do have a lot of request for the write that worths the cost of adding another function, we could add that later.

@klickverbot
Member

@kennytm If we provide write first, we won't ever have to add a second blessed function…

@kennytm
Contributor
kennytm commented Feb 4, 2012

@klickverbot That's because the 2nd one was already there. But if no one uses the write, it's just a waste.

@yebblies
Member
yebblies commented Feb 4, 2012

@kennytm It will sometimes make comments disappear if they are on the actual commits and the rebase replaces them, but I've never seen the comments on the actual pull request (like this one) disappear.

@kennytm
Contributor
kennytm commented Feb 19, 2012

Rebased since cc7f6eb broke the merge, and added __ctfeWrite.

(Also, combined all those ddocX-postscript.sh files since they share the same code anyway...)

@donc
Collaborator
donc commented Feb 23, 2012

This will cause segfaults. toChars() doesn't necessarily work on CTFE internal variables. Especially not pointers and ref parameters.

@kennytm
Contributor
kennytm commented Feb 23, 2012

@donc Can you given some explicit examples where the toChars() does not work? Thanks.

@donc
Collaborator
donc commented Feb 23, 2012

It cannot possibly work when given a pointer (except when the pointer is NULL). CTFE pointers don't have addresses.
int a = 2;
int *x = &a;
_ctfeWritefln(x); // what is this supposed to do?

@kennytm
Contributor
kennytm commented Feb 23, 2012

@donc Currently it prints

a

which might be misleading, but not segfault.

@donc
Collaborator
donc commented Feb 23, 2012

More complicated cases will segfault (eg, if a pointer inside an array points to the first element of that array), but that's not really the point -- it's just wrong. CTFE variables can contain values which have no representation outside of CTFE. Unfortunately, that applies to arrays, structs, and classes, which can also contain pointers.

@kennytm
Contributor
kennytm commented Feb 23, 2012

@donc That still doesn't crash:

class K
{
    void*[] r;
}

struct S
{
    void* p;
    K k;
}

int g()
{
    auto k = new K;
    void*[] arr = [];
    arr ~= arr.ptr;
    auto s = S(arr.ptr, k);
    __ctfeWriteln(s);
    return 0;
}

enum k = g();

void main()
{
}

prints

S([[][0LU]][0LU],TOK232)

If something does crash with __ctfeWriteln, it will crash pragma(msg, ...) too, because the two share the same function for printing an expression.


And it doesn't matter whether the value has no representation outside of CTFE, because __ctfeWriteln is mainly used for debugging CTFE code anyway. The function itself is a no-op outside of CTFE.

@donc
Collaborator
donc commented Feb 23, 2012

On 23 February 2012 15:58, kennytm
reply@reply.github.com
wrote:

@donc That still doesn't crash:

class K
{
   void*[] r;
}

struct S
{
   void* p;
   K k;
}

int g()
{
   auto k = new K;
   void*[] arr = [];
   arr ~= arr.ptr;
   auto s = S(arr.ptr, k);
   __ctfeWriteln(s);
   return 0;
}

enum k = g();

void main()
{
}

prints

   S([[][0LU]][0LU],TOK232)

Which is complete junk.
Trust me, other cases will crash.

If something does crash with __ctfeWriteln, it will crash pragma(msg, ...) too, because the two share the same function for printing an expression.

No. pragma(msg) cannot access CTFE internal variables.

And it doesn't matter whether the value has no representation outside of CTFE, because __ctfeWriteln is mainly used for debugging CTFE code anyway. The function itself is a no-op outside of CTFE.

'No representation outside of CTFE' means 'No representation which is
suitable for display to the programmer'.
It shouldn't be exposing implementation details.

@kennytm
Contributor
kennytm commented Feb 23, 2012

@donc Sorry I don't trust you ☺. If DMD segfaults, it would be a bug of DMD (e.g. bug 7568), not this pull request.

kennytm added some commits Aug 7, 2011
@kennytm kennytm fix Issue 3952 part 4: Add __ctfeWrite at for printing at intepret time.
This is similar to #237, but now it doesn't "rely" on Phobos. Phobos's writeln
can still use #237's behavior by using something like

    void write(T...)(T x) {
      if (__ctfe)
        __ctfeWrite(x);
      else
        stdout.write(x);
    }
6d2dcfe
@kennytm kennytm Added __ctfeWrite; Test cases: Combine all compilable/*-postscript.sh…
… files.
67d4435
@kennytm kennytm Test cases for __ctfeWrite[ln].
117447e
@MartinNowak
Member

Please consider to change the implementation so that __ctfeWriteln only accepts string arguments.

@kennytm
Contributor
kennytm commented Mar 29, 2012

@dawgfoto Why? pragma(msg, ...) supports any arguments, std.stdio.writeln supports any arguments, why __ctfeWriteln should only support string argument?

@MartinNowak
Member

Yes ctfeWriteln should do the same as stdio.writeln, i.e. formatting value by using conversion
functions. Most of which can be done using conv.to or formattedWrite.
This ensures that you inspect values and types only through their
public interface and not by any compiler internal functions.

int ctfe()
{
    import std.conv;
    int i;
    __ctfeWriteln(to!string(i));
    return 0;
}
enum trigger = ctfe();

prints:
['0'][0LU..1LU]
Which is the current state of an array literal slice?

CTFE literals are in a not-yet-frozen state during interpretation
which makes them break some invariants of the rest of the compiler.
See Bug 7784 for an example.

@donc
Collaborator
donc commented Mar 29, 2012

This ensures that you inspect values and types only through their public interface and not by any compiler internal functions.

That's not a problem, using toChars() is exactly the same thing you see in any error message (and it is what pragma(msg) uses). The problem is that in CTFE I've abused some of the expression types, and the existing toChars() doesn't cope with that, which is why I'd rather this didn't get merged just yet. It's not too hard to fix, because the number of expression types in CTFE is very small (int, float, complex, stringliteral, arrayliteral, struct, class, delegate, AAliteral, pointer to array, pointer to variable, slice of array).
OTOH allowing only strings would make it really slow for anything else, extremely slow for floating point, and it would be impossible to print pointers. I see this almost solely as a debugging aid, and that would cripple it for that.

@WalterBright
Member

Can this functionality be done with a combination of testing __ctfe and pragma(msg) ?

@yebblies
Member

@WalterBright No, pragma(msg) runs during semantic, not every time the statement is interpreted.

@klickverbot
Member

@WalterBright: What @yebblies said – the value of __ctfeWrite is in that it allows printing of »runtime« values during CTFE. pragma(msg, ...) would only print the variable/expression name once during semantic analysis.

@andralex
Member

ping on this

@andralex
Member

reping

@IgorStepanov
Contributor

What is the resolution on this issue? Do __ctfeWrite should accept all types arguments (and process they into DMD) or __ctfeWrite should accept only one string and __ctfe writeln version should use a format function?

@timotheecour

could we just make write/writeln work at CT (possibly in terms of __ctfeWrite) and not make it a special symbol? Ie, std.stdio would have to import std.ctfe module to access __ctfeWrite.

@quickfur
Member

@timotheecour I like the idea of implementing std.stdio.writeln in terms of ctfeWrite during CTFE. It's better to avoid the proliferation of more special __xxx symbols in the language. As long as ctfeWrite can output strings, that's good enough, let std.format take care of the formatting / conversions.

@bearophile

The basic need is to print strings, without newline at the end.

If you only have a function that always adds the newline (as puts and pragma(msg) do) but you don't want to print the newline (because you are printing some complex structure like a matrix, or a tree, or more one piece at a time) then in some cases you are forced to build a large string at compile-time before printing, and this is quite bad, also because currently at compile time the memory management of large strings not good. Having a second tiny function that also prints the newline is handy.

If you also want to support the printing of things different from strings, this is handy, but if Don says this could cause problems, then we can live without it, it's not an essential feature, because you can often call to!string first.

@bearophile

This feature is quite needed, what's missing before it's added to dmd?

@jcrapuchettes
Contributor

Do the merge conflicts and failed tests just need to be fixed for inclusion?

@quickfur
Member

ping @donc
Are we going to move ahead with this, or should we just close this as rejected?

@yglukhov
Contributor

Ping and +1 to implement ctfeWrite instead of ctfeWriteln, which may be trivially implemented on top of ctfeWrite.

@MartinNowak
Member

As mentioned above I think __ctfeWrite should only accept string arguments. The for printing non-string arguments we could adopt std.stdio.writeln to work in CTFE.

@IgorStepanov
Contributor

As mentioned above I think __ctfeWrite should only accept string arguments. The for printing non-string arguments we could adopt std.stdio.writeln to work in CTFE.

BTW, float formatting can be implemented in CTFE now, because yl2x has been implemented. There still alive only one objection: some one may want to print a pointer at CTFE (anyone unique object id). However we may add a special debug function string ctfePointerToString(void*) if it will be consider needed. Thus we may implement ctfeWrite only for string and add pointer hack in future if it will be needed.

@bearophile

Allowing __ctfeWrite to write only strings is acceptable as first step. It's a reasonable scope reduction to push __ctfeWrite inside the compiler, and it leaves space to add a successive __ctfeWritef/__ctfeWritefln with formatting of different types.

Printing pointers at compile-time is probably useful because if you can allocate memory at compile-time you also want to print the pointers to debug, like to find the ones that are equal to each other. But this need is much less significant than having a good enough string printing.

Regarding the name __ctfeWrite is it necessary to have the two leading underscores? For standard stuff that's supposed to be present in all compilers like ctfeWrite I'd prefer a nicer name without those underscores.

@klickverbot
Member

Regarding the name __ctfeWrite is it necessary to have the two leading underscores?

Yes, as it must not collide with user-defined names.

@MartinNowak
Member

Allowing __ctfeWrite to write only strings is acceptable as first step. It's a reasonable scope reduction to push __ctfeWrite inside the compiler, and it leaves space to add a successive __ctfeWritef/__ctfeWritefln with formatting of different types.

Everything else can be implemented using std.stdio.writeln and std.stdio.writefln.

@bearophile

@klickverbot: > Yes, as it must not collide with user-defined names.

I think in this case the risks of a collision can't justify giving a so much bad looking name. If an user defines a ctfeWritef then his/her name definition overwrites the built-in function. What's bad in this? It's a quite uncommon name...

@MartinNowak
Member

It's also __ctfe and you aren't supposed to use __ctfeWrite directly but writeln and writefln.

@IgorStepanov
Contributor

I suggest simply implement ctfeWrite with one string argument. All other logic can be implemented inside library. Lets do it so and will see how it will work.
We will able to add new functionality at any time.

@MartinNowak
Member

13 participants

Anyone willing to actually do it?

@IgorStepanov
Contributor

@MartinNowak Ok, I'll do it once I find the time. (1-2 weeks, I hope).
O.T. When you finish review and merge #3904?

@veelo
veelo commented Oct 19, 2015

Just to let you good people know, I wish this was available today...

@veelo veelo referenced this pull request in PhilippeSigaud/Pegged Nov 24, 2015
Closed

introspection.d infinite recursion during CTFE. #166

@MartinNowak
Member

Closed due to inactivity and wrong approach of the PR, as we now have fully CTFEable formating functionality in phobos.
Would accept a PR for a __ctfeWrite that just prints strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment