Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement pragma(ctfe) [alternative to #11007] #11014

Closed
wants to merge 9 commits into from

Conversation

jpf91
Copy link
Contributor

@jpf91 jpf91 commented Apr 8, 2020

A proof-of-concept alternative to #11007 , based on that PR.

Instead of detecting assert(__ctfe), this one introduces pragma(__ctfe). Compared to #11007 , in this one the information that a function is used only in CTFE is available earlier in the semantic phase. This allows the check to be integrated with -betterC checks and allows all test to work in betterC mode:

// REQUIRED_ARGS: -betterC

pragma(ctfe)
string ctfeOnly(string x, string y)
{
    return (x ~ " " ~ y);
}

pragma(ctfe)
string ctfeOnlyIn(string x, string y)
{
    return (x ~ " " ~ y);
}

pragma(ctfe)
string typeinfo(string s)
{
   return typeid(s).stringof ~ s;
}

pragma(ctfe)
string exceptions()
{
    try
    {
        throw new Exception("");
    }
    catch(Exception e) {}
    return "";
}

import core.stdc.stdarg;
pragma(ctfe)
int varargs(char c, ...)
{
    return 0;
}

Whether this PR or the other is merged in the end, I strongly suggest that we make sure least the test cases above work fine, to integrate this properly with betterC.

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @jpf91! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#11014"

@jpf91 jpf91 changed the title WIP: Pragma CTFE Alternative PoC: Pragma CTFE Alternative Apr 8, 2020
Copy link
Member

@Geod24 Geod24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! It does look better and self-contained. I agree that D already has enough magic cases.

// REQUIRED_ARGS: -betterC
// POST_SCRIPT: compilable/extra-files/ctfeonly-postscript.sh
// I guess the platforms below don't have nm
// DISABLED: win32 win64 osx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@jpf91 jpf91 Apr 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, let's see whether I can get this to work on windows. Do you know how to detect the OS in postscripts? But let's see, maybe uname works.

Copy link
Contributor

@MoonlightSentinel MoonlightSentinel Apr 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the OS variable, see test/README.md

src/dmd/e2ir.d Outdated Show resolved Hide resolved
changelog/assert_ctfe.dd Outdated Show resolved Hide resolved
changelog/assert_ctfe.dd Outdated Show resolved Hide resolved
@marler8997
Copy link
Contributor

Do you think there's any way we could support declaring a function as CTFE from another library?

For example, say I have a betterC program, but I want to call a function some_function from phobos that isn't pragma(ctfe)...would it be easy/possible to support something like pragma(ctfe, some_function)? That way, the function definition doesn't have to limit itself to ctfe-only, but if your application does not support it at runtime, it could still provide a pragma to be able to call it during ctfe.

@PetarKirov
Copy link
Member

@marler8997 The only real solution to this problem that I see what I proposed in the forums:
https://forum.dlang.org/post/xbllqrpvflazfpowizwj@forum.dlang.org, https://forum.dlang.org/post/nvvgrdlucajshjngdjlj@forum.dlang.org

@jpf91 jpf91 changed the title PoC: Pragma CTFE Alternative Implement pragma(ctfe) [alternative to #11007] Apr 9, 2020
@jpf91
Copy link
Contributor Author

jpf91 commented Apr 9, 2020

@marler8997 @PetarKirov
I think in this case, there's a simpler solution.

The first thing to note is that DMD does already not complain if you use external functions in a CTFE-only context:

import phobos;
void main()
{
    mixin(generateMixin("int a = 42"));
}

Where phobos has been compiled without -betterC and the example above is compiled with -betterC. This works just fine, as DMD does not try to emit functions from external modules. So you don't have to do anything for functions. Now if generateMixin is a template, DMD tries to emit it when compiling the above snippet and you run into -betterC errors.

Adding a pragma which affects other symbols is something quite new and probably controversial. And it would be strange, as it only affects templates, not functions.

However, the solution here is actually quite simple: If a template is only instantiated from CTFE contexts, do not emit its symbols. This is similar to what speculative instantiation already does. E.g. this:

import test2;

void main()
{
   static if(__traits(compiles, generateMixin("int a = 42")))
       pragma(msg, "it does");
}

It compiles (somehow avoids -betterC checks, I'm not sure if that's a bug) and does not emit anything. We want exactly the same thing if a template is instantiated from a CTFE context. In the worst case, we might have to do semantic twice (first time: CTFE context, -betterC disabled, second time if function is used in other place in a runtime context, this time -betterC enabled). But apart from that detail, it should work just like speculative instantiation.

This has the added benefit, that all templates benefit, without having to manually mark them as pragma(ctfe). pramga(ctfe) is still needed for non-template functions. Although it's then not as useful.... OTOH I don't know how difficult it is to implement this "emission suppression". I'll have a quick look at it this weekend.

@n8sh
Copy link
Member

n8sh commented Apr 16, 2020

Maybe pragma(ctfe_only) instead? Someone could easily misunderstand pragma(ctfe) as enabling CTFE-ability rather than meaning the function only exists during CTFE.

@adamdruppe
Copy link
Contributor

adamdruppe commented May 6, 2020

EDIT: see below comments, i retract this basically as inaccurate.

Instead of as an alternative to the other one, I suggest both. Call the other PR "ctfe inference" if you will but do check the body.

Why? A new pragma on the declaration is not compatible across dmd versions. Once you add it to code, you lock your code out of all previous versions of the compiler. And since it is attached to a declaration, the only way to version it out is to copy/paste the whole declarations which is obnoxious, and even if you try to factor the body out to another function, you can't mark that other function with the pragma so it defeats the purpose.

And if any one of your libs try to use the new thing, it is liable to force a version-lock on its users too....

@MoonlightSentinel
Copy link
Contributor

Why? A new pragma on the declaration is not compatible across dmd versions. Once you add it to code, you lock your code out of all previous versions of the compiler.

That's not entirely true, dmd has -ignore to ignore unsupported pragmas since 2.013.

But adding this flag might be cumbersome depending on your current build system (if any).

@adamdruppe
Copy link
Contributor

huh you know i either never knew that or forgot about it.

and gdc has -fignore-unknown-pragmas Ignore unsupported pragmas.

and ldc calls it --ignore

OK, I take that back, well, 3/4 of the way back, it is still a slight annoyance but can document that for users. so i think i can live with it.

@wilzbach
Copy link
Member

wilzbach commented May 6, 2020

As I imagine pragma to be used more often in the future, e.g. pragma(noreturn), maybe the default should be changed to ignore unknown pragmas by default for better backwards compatibility in the future? Or at least allow a library to allow ignoring failures for its modules, s.t. it can start using newer features without any problems to users with an older compiler.

Anyhow, that's a bit OT to this PR as there are a lot of other good application of per-module compiler flags (like safe-by-default or other preview flags).

generated object files. Although the linker usually removes those unused functions,
this still causes some problems:

* The function must pass semantic like any other function (i.e. with `-betterC`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take care of trailing whitespace in here.
@jpf91

@AndrewEdwards
Copy link
Contributor

@jpf91 - This requires your attention. Please address all concerns and update to resolve all test failures.

@dkorpel dkorpel mentioned this pull request Apr 28, 2022
@dkorpel
Copy link
Contributor

dkorpel commented Apr 28, 2022

Rebooted here: #14036

@dkorpel dkorpel closed this Apr 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.