Bug 7417 – One-definition rule for version specification - allow version expressions

Status
NEW
Severity
enhancement
Priority
P4
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2012-02-01T03:51:32Z
Last change time
2024-12-13T17:58:14Z
Keywords
spec
Assigned to
No Owner
Creator
Don
See also
https://issues.dlang.org/show_bug.cgi?id=12887
Moved to GitHub: dmd#17539 →

Comments

Comment #0 by clugdbug — 2012-02-01T03:51:32Z
This enhancement adds a new syntax for version specifications, which would allow us to eliminate the bird's nest of version statements that occurs when code has a complicated version dependency. The new syntax makes version specifications look like boolean variable declarations: version identifier = expression; It would become illegal to reference a version identifier which hasn't been declared. The spec already says that version declarations may only occur at module scope; this new form of version specification would additionally be disallowed inside version blocks (this enforces the one-definition rule). version identifier = extern; means that the identifier is externally specified (either on the command line, or as a compiler built-in). version VersionIdentifier = VersionExpression; extern version VersionIdentifier; // means this version is set from command line, or is a compiler built-in VersionExpression: VersionExpression && VersionExpression VersionExpression || VersionExpression !VersionExpression ( VersionExpression ) VersionIdentifier true false extern version(A) { version = AorNotB; } version(B) { } else { version = AorNotB; } becomes: version AorNotB = A || !B; ---- Note that this is backwards-compatible, it doesn't collide with the existing syntax. To get the full benefit from it, though, we would need to eventually disallow existing version specifications from being inside version blocks. version = XXX; outside of a version block would be the same as version XXX = true; so most code would continue to work.
Comment #1 by code — 2013-01-30T06:18:10Z
Now we've accidentally led the necessary discussion in a pull request. https://github.com/D-Programming-Language/d-programming-language.org/pull/243 Maybe we should archive this here.
Comment #2 by code — 2013-01-30T07:41:11Z
> WalterBright >A far more robust and organized approach is to figure out just what you're trying to abstract away in these version blocks, and then abstract it away as a struct, class, function, or import. Then, >version (B) >feature() { return foo(); } >else version (C) >feature() { return foo(); } >else version (D) >feature() { return creature(); } >else >static assert(0, "version not supported"); >This is clear, simple, organized, and forces the programmer adding E to check each one of those, instead of assuming they will work. >I know that D users will continue to try and write C style version blocks, but I will continue to try and show a better way. I also am painfully aware that druntime/phobos have turned to C style versioning, despite my exhortations. It should be fixed, not embraced. Please do. It's an important design principle, deserves a proper name and getting popularized. It takes some getting used to it because it apparently contradicts other design principles. But we DO use it in druntime/phobos all over the place. I hope you can write an article or give a talk about this at some point. I do not agree though that it's the right approach for all problems. Take ELFOBJ in DMD as an example. >#define ELFOBJ (TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) If what you want to specialize is ELF code you need exactly this abstraction. It solves the maintenance issue of having to restate it over and over again, therefor I mentioned DRY. Adding a new ELF target could use a compilation/fix/test-cycle approach and testing has become the method of choice for most projects. >The recent attempt to replace in the DMD source code a bunch of the operating system predefines with POSIX was a failure, and the reason for the failure was a fine example of why the C approach is inferior and why it isn't supported in D. I didn't follow that but as you said before it would have been naive to think everything would just work. For POSIX to work you need two sides, a fully compliant implementation and POSIX correct client code. This is doomed to fail but it doesn't make POSIX a bad abstraction layer. The problem is that forcing people to use something they feel is a bad match to their problem won't make this popular. This is the reason why "final switch" is a winner, it optionally allows people to improve their code correctness but it doesn't get in the way. It would be more fruitful to warn/error about a missing default case in an if-else ladder. If you thoroughly read through Dons proposal you see that he enforces to declare all used version identifiers before usage. This would open a door for better compile time checks. Maybe we could even check that no value combination of the induced extern version identifiers would lead to taking a branch of an if-else ladder. This would be something I'd call a worthy D implementation.
Comment #3 by code — 2013-01-30T08:00:22Z
And from a personal experience of a vibe.d app for mips-openwrt. Having to solve all problems at once was the reason I spend 2 weeks trying to implement what I wanted in one of the already supported languages. I then spend 1.5 days on my vibe app and more than 1 week on understanding glibc's and ulibc's header structure and compiling/fixing/debugging druntime MIPS. Being faced with tons of compilation issues in a complex OpenWRT/GCC/GDC build chain is not funny. In the end most of the issues were a missing "static if(0, "Unsupported platform")" in the default branch of a "static if" or "version" ladder, easy to fix but unnecessary. Some other issues were outright bugs like a stat64 redeclaration in phobos's std.file, I think that got fixed in the meanwhile.
Comment #4 by code — 2013-01-30T09:33:31Z
Scratch the version combination idea it's too complex and doesn't yield the wanted result. What might work out really well is to detect version-static-if-else ladders as constructs and warn if no branch was taken. This depends on the current set of defined version identifiers and static if conditions and would have been an actual game changer for porting.
Comment #5 by code — 2013-01-30T09:39:24Z
IMHO we should support version imports. When an extern identifier is not specified we could perform an import search and pickup public version declarations. Hijacking protection included of course.
Comment #6 by witold.baryluk+d — 2016-04-26T21:01:21Z
Hi. Any update on this. I was just trying to compiled phobos using gdc on arm64, and of course first thing that failed was a static assert in libdruntime/core/sys/posix/sys/socket.d that do have about 10 different semi duplicated version branches. I would really want to say: version (ARM || AArch64 || PPC || PPC64 || MIPS32 || MIPS64 || X86 || X86_64) { ... } else version (SomethingSpecial) { ... } else { static assert(0); } That would be good enough in most cases. Support for more elaborate conditions would be nice too. instead of version (A) { } else { somethign special. } version (!A) { something special. } or version (A) { version (B) { something. } } version (A && B) { something. } It is probably possible to implement what I want using mixing and putting common code in the template, and then expand version (ARM || AArch64 || PPC || PPC64 || MIPS32 || MIPS64 || X86 || X86_64) { into separate branches with mixed-in template. Still I fell, explicit booleans support on versions would be nicer. BTW. The mentioned file also shows almost the same enums in the OSX and FreeBSD branches, so maybe in this case it is worth doing this via mixin, as the OSX and FreeBSD branches cannot be really folded easily into one version (OSX || FreeBSD) branch. Same with Android (with do have same enum values as Linux).
Comment #7 by b2.temp — 2019-06-30T08:05:25Z
*** Issue 19495 has been marked as a duplicate of this issue. ***
Comment #8 by dfj1esp02 — 2019-09-16T09:00:39Z
*** Issue 20210 has been marked as a duplicate of this issue. ***
Comment #9 by dfj1esp02 — 2021-06-10T18:42:49Z
*** Issue 21999 has been marked as a duplicate of this issue. ***
Comment #10 by dfj1esp02 — 2021-06-10T18:45:02Z
Workaround: --- template Defined(string s) { mixin(`version(`~s~`)enum Defined=true;else enum Defined=false;`); } struct SVersion { alias opDispatch(string s)=Defined!s; } enum Version=SVersion(); int main() { static if(Version.OSX)writeln("running OSX"); else writeln("no"); return 0; } ---
Comment #11 by dfj1esp02 — 2021-06-10T18:57:52Z
Minimal implementation with static opDispatch: --- struct Version { template opDispatch(string s) { mixin(`version(`~s~`)enum opDispatch=true;else enum opDispatch=false;`); } } static if(Version.OSX || Version.linux){} else{} ---
Comment #12 by dfj1esp02 — 2021-06-10T19:09:54Z
I'm in favor of addition of version(true) and version(false), because "all" and "none" aren't keywords and I'm never sure if I typed them correctly, but failure to type them correctly is often silent. Also version(true) and version(false) feel more intuitively understandable than version(all) and version(none).
Comment #13 by robert.schadek — 2024-12-13T17:58:14Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/17539 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB