Issue 3952 - pragma(msg,...) has bugs + alternative idea
Summary: pragma(msg,...) has bugs + alternative idea
Status: RESOLVED WORKSFORME
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P2 enhancement
Assignee: No Owner
URL:
Keywords: pull
Depends on:
Blocks:
 
Reported: 2010-03-13 05:11 UTC by bearophile_hugs
Modified: 2018-05-17 07:44 UTC (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description bearophile_hugs 2010-03-13 05:11:28 UTC
The D2 docs state about pragma(msg, ...):
Prints a message while compiling, the AssignExpressions must be string literals:
pragma(msg, "compiling...");

I think that means it must accept string literals only, and produce a syntax error in all the other cases. In practice if you look at the following program the pragma(msg, foo0()); prints the "this is a test" string. This is useful, but in many other cases this doesn't work or works in strange ways:


string foo0() {
    return "this is a test";
}
string foo1() {
    return "";
}
string foo2() {
    string result;
    return result;
}
string foo3() {
    return [];
}
pragma(msg, foo0());
pragma(msg, foo1());
pragma(msg, foo2());
pragma(msg, foo3());
string hello = "red";
pragma(msg, hello);
pragma(msg, 12);
void main() {}



The compile-timeoutput of that program is:
this is a test

null
[]
hello
12


So I think pragma(msg,...) needs some debugging.

-------------

This program doesn't compile:


enum int x = 10;
static if (x)
    pragma(msg, "true");
else
    pragma(msg, "false");
void main() {}



It prints the error:
test.d(4): Declaration expected, not 'else'

I can understand the cause of that error, but to improve the usefulness of the pragma and avoid errors like that one I think the compiler can rewrite the pragma(msg, ...) as:

{pragma(msg, ...);}

-------------

To increase the usefulness of the pragma(msg, ...) I suggest to not let it print a newline after the string.

-------------

D2 programs have a better and better CTFE, so the need for good enough printing is growing.

So I suggest to add a simple printing function that works in the same way both at compile-time in CTFE and at runtime. To keep things simple it can just print a string (literal or inside a variable) and it does NOT print a newline.

It can be used in a loop too, in CTFE too, to print many things (so it's different from a pragma(msg,...):

string foo(int i) {
    foreach (i, 0 .. 10)
        ctputs(ToString!(i));
}

Note that it's a true function, so there is no need for {}.

"ctputs" is just one of the possible names of this function.

Once such function is present, the pragma(msg,...) can probably be removed from the language.
Comment 1 bearophile_hugs 2010-08-06 09:34:59 UTC
In D1 there were no template constraints, so to test the template arguments I used to add some static asserts inside them. So a wrong template argument shows a error message written by me that explains why the instantiation has failed (unfortunately those error messages show the line number inside the template).

When a template constraint is composed of some different parts in &&, it's less easy to understand what condition has failed, so I miss the error messages written by me. This is an example (that probably I will simplify), there are four conditions, and for a person that has not written this code, and is just using Phobos, it's not immediately obvious what part has failed:


auto opBinary(string op, TOther)(TOther other)
  if (op == "~" && is(TOther == struct) &&
      (!__traits(compiles, { void isTuple(U...)(Tuple!U){} isTuple(other); }) ||
       distinctFieldNames!(T, TOther.TypesAndStrings)() )) { ...


There is a simple solution. The simple template constraints often don't need extra error messages, so they can be left as they are now.

Putting one or more error message inside a template constraints turns them into messy code, so in such cases it's better to move the tests elsewhere, in an external template/CTFE:

template IsGoodFoo(T) {
    static if (...) {
        enum bool IsGoodFoo = true;
    } else {
        ctputs("this is an error message");
        return false;
    }
}

void foo(T)(T x) if (IsGoodFoo!T) { ...


So the ctputs() is usable for the template constraints error messages too.
Comment 2 bearophile_hugs 2010-08-07 00:14:07 UTC
Andrei has asked if just a pragma(msg) is enough for the error template constraints.

In some situations it works. But to use pragma(msg) you have to guard it with a static if. So if the template constraint is a CTFE (that uses a normal 'if' instead of a 'static if') you can't use it. While ctputs() can be used, this shows the error message even if it's not required:

bool isGoodFoo(int x) {
    if (x > 10) {
        return true;
    } else {
        pragma(msg, "no good");
        // ctputs("no good");
        return false;
    }
}
void foo(int N)() if (isGoodFoo(N)) {
}
void main() {
    foo!(20)();
}
Comment 3 BCS 2010-08-08 07:23:18 UTC
What about this case:

auto Fn(T...)(T t)
{
  foreach(Te; T) // compile time foreach
  {
    pragam(msg, "processing: " ~ Te.stringof)
    ... ctfe valid code
  }
}

void main()
{
   Fn!(int, int, float)(1,2,3.14); // will be evaluated at _run time_
}

//
 expected CT output:

processing: int
processing: int
processing: float

//
 expected runtime output: none
Comment 4 bearophile_hugs 2010-08-08 08:17:34 UTC
I don't understand what you exactly mean, but similar problems can be solved by:

if (__ctfe) ctputs("...");

Or:
if (!__ctfe) ctputs("...");
Comment 5 bearophile_hugs 2011-07-25 08:35:25 UTC
See also the discussion here:
https://github.com/D-Programming-Language/dmd/pull/237
Comment 6 BCS 2011-07-25 10:13:37 UTC
I don't have a github account so I'll comment here: There is use for ways to create output at runtime, CTFE time and static expansion time. For instance this function:

int TemplateCTFE(T...)(int j) {
  for (int i = 0; i < j; i++) {
    foreach(t; T) {
      pragma(msg, t.stringof);
    }
  }
}

When called like this from a CTFE context:

TemplateCTFE!(int, char, float)(5);

will only print "int\nchar\float" once rather than 5 times. IMHO this static-expansion-time ouput is more valuable than a CTFE time output (that would output 15 line from that call). OTOH, having both would be really nice.
Comment 7 bearophile_hugs 2011-07-25 10:47:25 UTC
(In reply to comment #6)

> OTOH, having both would be really nice.

There are no plans to remove pragma(msg) :-)
Comment 8 kennytm 2012-02-19 07:34:07 UTC
DMD pull #692.

https://github.com/D-Programming-Language/dmd/pull/692
Comment 9 github-bugzilla 2012-02-20 13:52:07 UTC
Commits pushed to master at https://github.com/D-Programming-Language/druntime

https://github.com/D-Programming-Language/druntime/commit/52494246e8f7ac08c3f5f40d379ced763e8103e3
fix Issue 3952 part 4: Add __ctfeWrite and __ctfeWriteln.

This is the druntime part of the fix.

https://github.com/D-Programming-Language/druntime/commit/58afd8b349567873777387119dc36a9e62e79457
Merge pull request #155 from kennytm/bug3952d_ctfeWriteln_again

fix Issue 3952 part 4: Add __ctfeWrite and __ctfeWriteln.
Comment 10 Andrej Mitrovic 2013-02-05 13:54:53 UTC
Is this resolved now?
Comment 11 bearophile_hugs 2013-02-05 14:57:11 UTC
(In reply to comment #10)
> Is this resolved now?

- - - - - - - - - - - - - - - - -

This doesn't work:

__ctfeWriteln("hello");
void main() {}


It gives:

temp.d(1): Error: unexpected ( in declarator
temp.d(1): Error: basic type expected, not "hello"
temp.d(1): Error: found '"hello"' when expecting ')'
temp.d(1): Error: no identifier for declarator __ctfeWriteln(int)
temp.d(1): Error: semicolon expected following function declaration
temp.d(1): Error: Declaration expected, not ')'

- - - - - - - - - - - - - - - - -

While this gives no errors but prints nothing:

int foo() {
    __ctfeWriteln(1);
    __ctfeWriteln("hello");
    return 0;
}
enum x = foo();
void main() {}


- - - - - - - - - - - - - - - - -

I don't like the names __ctfeWriteln and __ctfeWrite, they are ugly. It's much better to call them ctWriteln and ctWrite. They are meant to be a clean and handy feature of the D language, not some temporary compiler-speciic hack.

- - - - - - - - - - - - - - - - -

I don't see the D docs about those two functions in the D site. They need to be documented.

- - - - - - - - - - - - - - - - -

So this is not resolved yet.
Comment 12 Andrej Mitrovic 2013-02-05 15:04:49 UTC
Ok I can see now the Druntime pull was merged by accident. We're waiting for https://github.com/D-Programming-Language/dmd/pull/692
Comment 13 bearophile_hugs 2013-02-05 15:45:38 UTC
(In reply to comment #12)
> Ok I can see now the Druntime pull was merged by accident. We're waiting for
> https://github.com/D-Programming-Language/dmd/pull/692

Right.


I don't know if this is supposed to work:

__ctfeWriteln("hello");
void main() {}


Regarding the bad __ctfeWriteln/__ctfeWrite names, I don't know what to do.
Comment 14 Dmitry Olshansky 2018-05-17 07:44:37 UTC
This is getting out of scope real quick.

First the code:

string foo0() {
    return "this is a test";
}
string foo1() {
    return "";
}
string foo2() {
    string result;
    return result;
}
string foo3() {
    return [];
}
pragma(msg, foo0());
pragma(msg, foo1());
pragma(msg, foo2());
pragma(msg, foo3());
//string hello = "red";
//pragma(msg, hello);
pragma(msg, 12);
void main() {}


Now works as expected no [] literals. Except for static `hello`.

__ctfeWrite is a separate issue so no point in doing it here. 
Lastly any bugs in pragma(msg, ...) are welcome as specific bugs.