Bug 12892 – extern(C): label confined to version scope

Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-06-11T20:02:00Z
Last change time
2014-06-20T06:05:39Z
Assigned to
nobody
Creator
markisaa
See also
https://issues.dlang.org/show_bug.cgi?id=12894

Comments

Comment #0 by markisaa — 2014-06-11T20:02:22Z
If I write: version(Win32) { extern(Windows): } else { extern(C): } Then the functions below this version block are unaffected. In particular, the extern does not apply to anything after the close curly. This also happens if I remove the curly braces from that code. If I just do: extern(C): without any version block, everything works fine. Similarly, if I do: version(Win32) { enum isWin32 = true; } else { enum isWin32 = false; } static if (isWin32) { extern(Windows): } else { extern(C): } Then it also does *not* work as expected. The extern label does not go beyond the close curly. If I remove the curly braces from the static if part of that, I get: $ dmd cversion.d cversion.def odbcinst.d sqlucode.d sqlext.d sql.d sqltypes.d -m64 -shared cversion.d(44): Error: Declaration expected, not 'else' cversion.d(59): Error: Declaration expected, not 'if' cversion.d(67): Error: Declaration expected, not 'if' cversion.d(73): Error: no identifier for declarator szConnStrOut[cbConnStrOut - 1] cversion.d(73): Error: Declaration expected, not '=' cversion.d(74): Error: unrecognized declaration
Comment #1 by markisaa — 2014-06-11T20:11:35Z
Part of my description is in error: If I remove the curly braces from the first bit of code (the version/extern stuff), it gives me the same error messages that I got when removing the curlies from the static if version.
Comment #2 by bugzilla — 2014-06-11T20:40:25Z
This has come up in the forums: http://forum.dlang.org/thread/mailman.1308.1401315810.2907.digitalmars-d-announce@puremagic.com?page=3#post-lmdoga:241ab3:241:40digitalmars.com Which I'll quote: On 5/30/2014 5:37 AM, Steven Schveighoffer wrote: > On Thu, 29 May 2014 21:15:21 -0400, deadalnix <[email protected]> wrote: > >> On Thursday, 29 May 2014 at 19:06:15 UTC, Steven Schveighoffer >> wrote: >>>> Static if is certainly NOT an attribute, it doesn't make any sense. >>> >>> Well... it sorta does. static if does not introduce a new scope, even with >>> {}, and this only happens with attributes. >>> >>> -Steve >> >> in which case >> >> static if(cond) { >> immutable: >> } >> >> int x; >> >> should not create x as immutable if cond is true. The current >> behavior is not consistent with attribute either. > > Ugh, that is really bad. It shouldn't do that. Is that intentional? Yes. Semantic scope and lexical scope are different things. The ':' thing applies to the remaining statements in the lexical scope. 'static if' does not create a new semantic scope, even though the { } suggests it does. There have been several suggestions to make 'static if' apply independently of the rest of the grammar, i.e. allow things like: int static if (cond) * else [ ] foo; // conditionally make foo a pointer or an array I think we can agree that looks awful, but it is the same thing as suggesting that the 'immutable:' above extend outside of its lexical scope. You might ask "why is semantic scope different from lexical scope" and the reason is simply that 'static if' would not be very useful if that were the case.
Comment #3 by bugzilla — 2014-06-11T20:56:23Z
A few comments: 1. This is working as designed, so I've marked this as an enhancement request rather than a bug. 2. It applies to all platforms and memory models, so reclassified accordingly. 3. Changing the behavior will break an unknown amount of existing code. 4. Changing the behavior in the compiler will not be simple, as it will no longer be a parsing/grammar issue but a semantic one. 5. extern(Windows) on non-Windows platforms should produce C linkage behavior anyway, so this particular usage does not seem to be necessary. I also have to question why one would need extern(Windows) for any reason other than interacting with Windows API functions, which don't exist on other platforms. 6. If you really need this behavior, it can easily (but not prettily) be done using string mixins: enum string mydeclarations = q{ int a; void foo(); }; version(Win32) { enum string linkage = "Windows"; } else { enum string linkage = "C"; } mixin( "extern(" ~ linkage ~ ") {" ~ mydeclarations ~ "}" );
Comment #4 by markisaa — 2014-06-11T21:03:14Z
Thanks for the quick reply Walter! I'm afraid that I'm still confused however; the explanation seems tangential. I'm aware that the curly braces from the static if are present for grouping rather than [semantic?] scoping. It is precisely because of this that I would expect use of extern(C): as a label to transcend the curly braces. Otherwise, the power to do something like version(X) { extern(C): } seems like nonsense that should be flagged by a linter at minimum. I agree that the ability to use static if anywhere seems like a gross expansion of its current role. Perhaps it is that I have trouble thinking in terms of grammars, but that seems much more dramatic/drastic of a change than allowing use of things like extern(C): or immutable: to escape version/static if grouping. Is there no sensible middle ground? I suppose that leaves me with another question, which is how might I achieve the result I'm looking for: to conditionally compile with extern(Windows) or extern(C) depending on platform for a large number of functions? Is today's answer to copy paste the version() {} else {} to all of them? To use a mixin/mixin template and repeat that instead?
Comment #5 by markisaa — 2014-06-11T21:07:28Z
Did not catch your last post before writing my own. I think that answers most of my queries. Andrei tells me he is following up with you, so I will see what comes of that.
Comment #6 by bugzilla — 2014-06-11T21:50:58Z
Currently, extern(Windows) on Win64 will mangle like C. Unfortunately, extern(Windows) on non-Windows systems will mangle oddly. So there are multiple solutions: 1. use string mixins as suggested 2. use template mixins as you suggested 3. copy/paste the declarations as you suggested 4. change extern(Windows) on non-Windows systems to mean extern(C) 5. Andrei suggested recognizing: version (identifier) { attribute: } else { attribute: } as special, since currently it has no effect. What do you think?
Comment #7 by markisaa — 2014-06-11T23:11:15Z
I must've closed the tab before submitting my last reply: Solution 5 sounds ideal to me. It shouldn't have any backward compatibility issues and it also does the intuitive thing. Solution 4 also sounds reasonable, but comes with a couple of questions: 1) Will code break? 2) Can we ensure that this is well documented? I had enough trouble trying to find out what I already know about extern(Windows) and what it does; it's hard to find anything either via searching or perusing the Language Reference pages. For now I only actually *need* to develop for one platform, so I will likely just comment out the version lines and target Win64 directly. Thanks for the help!
Comment #8 by bugzilla — 2014-06-11T23:50:50Z
> 1) Will code break? See https://issues.dlang.org/show_bug.cgi?id=12894 > 2) Can we ensure that this is well documented? Sure!
Comment #9 by yebblies — 2014-06-12T01:03:55Z
(In reply to Walter Bright from comment #3) > > 6. If you really need this behavior, it can easily (but not prettily) be > done using string mixins: > > enum string mydeclarations = q{ > int a; > void foo(); > }; > version(Win32) { > enum string linkage = "Windows"; > } else { > enum string linkage = "C"; > } > mixin( "extern(" ~ linkage ~ ") {" ~ mydeclarations ~ "}" ); If you really need this behavior it can be done TRIVIALLY with extern(System): ...
Comment #10 by bugzilla — 2014-06-12T01:34:41Z
yebblies is right.
Comment #11 by markisaa — 2014-06-12T01:36:26Z
Indeed. Many thanks!
Comment #12 by bugzilla — 2014-06-20T06:05:39Z
Seems to be satisfactorily resolved.