|Posted by Jonathan Marler||PermalinkReply|
This is an informal proposal to add a versioning scheme to the D Language. If you'd like to take the time to read, please let everyone know your thoughts and suggestions for the idea. To Walter and Andrei, please let me know your thoughts, if you think the idea has merit and if so what the next steps should be (i.e. DIP/PR/More Resarch Discussion Needed).
As I've been thinking about various proposals and additions to the D language, it occurred to me that I'd like to be able to declare inside my code that it requires a certain version or certain features of the D language in order to work properly. For example, when I look at the changelog for DMD version 2.085, I see that support for Objective-C classes was added. However, I would be hesitant to use that feature until a certain amount of time has passed. This is because using it would make my libraries/applications only work with a small percentage of compilers in the world, those who have recently updated their compiler, and the error messages people would get could make it difficult to know that they just need to update their compiler for it to work with my code. Currently the only way to solve this problem is to wait a certain amount of time until I feel its reasonable that enough people will have updated their compiler.
I think we can do better.
My current idea is to implement a list of versions and feature names. Such a list could be dumped with something like:
> dmd --list-versions
The idea here is that once a feature has been implemented and has been enabled by default, the version would be updated and that particular feature would be assigned to that version. Also note that the version number represents a date, which allows the compiler to know when that version was implemented so it can know how "out-of-date" it is (i.e. 2 years, 1 month etc). You'll also notice that some features were not assigned a version number but were either assigned an "Implemented" or "NotImplmeented" tag. This indicates whether or the feature can be enabled, where "Implemented" means it can. Once a feature has been implemented and enabled by default, then it will be assigned a version number representing the date it was enabled by default.
Then we can add ways for modules to declare version/feature requirements, such as:
The way the compiler handles these pragmas can allow the compiler to print nice error messages when it can't satisfy the version/feature requirements of the module. For example, if a module declares `pragma(dlangVersionAtLeast, 2019_01_02)`, but it only support up to version 2018_05_06, then it can print an error message like:
Error: module 'x' requires dlang version '2019_01_02' but you're at version '2018_05_06'. Your compiler is 7 months too old to compile this code.
Notice that since the version has the date encoded into it, the compiler knows how old it is compared to the compiler version it needs to compile this module.
Since each version is also tied to a feature name, this would also allow modules to enable features even when they aren't turned on by default, i.e.
If possible, this could have the same affect as specifying `-dip1008` on the command-line. In some cases, there may be features that can't be enabled once you've already started compilation of modules, and in this cases you could get a nice error message like:
Error: module 'x' requires the 'NoGCExceptions' feature. Please provide the '-dip1008' option to compile this code.
Of course, if this versioning is enabled, each version could use a general command-line syntax such as
Along with these pragmas, another feature would be to allow code to obtain the version and/or check whether certain features can be enabled at compile time to work with multiple version of the lanaguage. So adding a couple traits like
And could be used like:
static if (__traits(tryEnableFeature, NoGCExceptions))
As D matures, these versioning mechanisms could alleviate some of the pain in introducing new features to the language by allowing the compilers of today to be aware of the timeline and feature names of features that are yet to be implemented. It also allows the code to declare what features it can work with and even allow it to work adapt based on the version of the compiler you are using. Furthermore, it provides the opportunity to support a consistent command-line syntax that will always work when a new feature is introduced.