Language Versioning

Jonathan Marler johnnymarler at gmail.com
Thu Apr 4 12:38:01 UTC 2019


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
2018_02_03 StaticForeach
2018_02_20 NoGCExceptions
2018_03_10 ObjectiveCClasses
Implemented StaticForeach
Implemented NamedParameters
NotImplemented CopyConstructor
NotImplemented ExpressionBasedContracts

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:

pragma(dlangVersionAtLeast, 2017_11_25);
pragma(dlangVersionUpTo, 2018_09_02);
pragma(requireFeature, ObjectiveCClasses);

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.

pragma(requireFeature, NoGCExceptions);

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
`--enable-feature=NoGCExceptions`.

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

__traits(tryEnableFeature, NoGCExceptions)

And could be used like:

static if (__traits(tryEnableFeature, NoGCExceptions))
{
     @nogc:
}

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.



More information about the Digitalmars-d mailing list