ID:2072419
 
Resolved
Using #undef right after a string that used embedded expressions with a #define macro could cause the macro not to be processed properly.
BYOND Version:510.1338
Operating System:Linux
Web Browser:Chrome 50.0.2661.75
Applies to:Dream Maker
Status: Resolved (515.1607)

This issue has been resolved.
Descriptive Problem Summary:
#undef-ing a defined value on the next non-blank line (or in a chain of #undef which starts at that line) after using it in a "[]" expression makes the "[]" expression not compile

Numbered Steps to Reproduce Problem:
1. Attempt to compile code below.

Code Snippet (if applicable) to Reproduce Problem:
/proc/foo()
#define FOO 1
#define BAR 2
switch(rand(1,2))
if(1) return "[FOO]"
if(2) return "[BAR]" // error: BAR: undefined var
#undef FOO
#undef BAR

/proc/bar()
#define FOO 2
return "[FOO]" // error: FOO: undefined var
#undef FOO


/proc/foo2()
#define FOO 1
#define BAR 2
switch(rand(1,2))
if(1) return FOO
if(2) return BAR // this is ok
#undef FOO
#undef BAR

/proc/bar2()
#define FOO 2
return FOO // this is ok
#undef FOO


Expected Results: Compilation success, since all defines are only used between being defined and undefined.

Actual Results: Compilation failure.

Does the problem occur:
Every time? Or how often? Every time.
In other games? N/A
In other user accounts? Unknown
On other computers? Unknown

When does the problem NOT occur? Unknown

Did the problem NOT occur in any earlier versions? If so, what was the last version that worked? Unknown

Workarounds: Insert a do-nothing statement before the #undef, eg .=.

edit: moved the #foo over so it highlights right
That's not how preprocessors work, they're handled before any code is compiled at all, the code comes afterwards, so essentially when you undef the define by the end of the process the code is compiled without that define, so it won't be able to use it.

That's the whole point of them, to handle certain things before the code is compiled and to conditionally compile code, they're not processed inline and shouldn't be.
Actually I think he may be right on this; in C++ a #define and #undef are both handled inline. The real question is whether DM does the same thing. If DM is truly pre-processing, then that's just the way it is; if it's doing it inline, then the #undef being handled prematurely is a bug.
As far as I've always known all of the # stuff is handled before the code is compiled. That's how Dan always explained it to me when I was learning bits and pieces from him way back, but how it works internally I'd have no idea. I can imagine by the end of the process when the code is finally being compiled out the define has already been kicked from the table.
In response to Nadrew
Nadrew wrote:
I can imagine by the end of the process when the code is finally being compiled out the define has already been kicked from the table.

I think the #define only matters during parsing. I don't think it's held onto after that step, since there's no more need for it.
In response to Nadrew
Nadrew wrote:
That's not how preprocessors work, they're handled before any code is compiled at all, the code comes afterwards, so essentially when you undef the define by the end of the process the code is compiled without that define, so it won't be able to use it.

Yes, I know that. The following should compile. It doesn't.

#define FOO 1
var/x = "[FOO]"
#undef FOO

In response to Lummox JR
Lummox JR wrote:
Actually I think he may be right on this; in C++ a #define and #undef are both handled inline. The real question is whether DM does the same thing. If DM is truly pre-processing, then that's just the way it is; if it's doing it inline, then the #undef being handled prematurely is a bug.

Given that the first below compiles and the second doesn't, I suspect it's a bug.
#define FOO 1
var/x = text("[]", FOO)
#undef FOO

#define FOO 1
var/x = "[FOO]"
#undef FOO
Bump. Encountered this again with my IRC bot's DM expression executor.

Still an issue as of 512.1388.
Code Snippet (if applicable) to Reproduce Problem:
#define NAME "xX_Mothblocks_Xx"

/proc/main()
return "[NAME]"

#undef NAME


Expected Results:
This would give the name.

Actual Results:
DM compiler version 514.1580
loading code.dm
code.dm:5:error: NAME: undefined var
code.dm.dmb - 1 error, 0 warnings (1/9/23 11:46 am)
Total time: 0:00

Workarounds:

Without the #undef, it works fine.
In response to Jaredfogle
Preprocessor commands are parsed before any code, you're effectively defining and undefining the macro before any code is compiled at all.
In response to Jaredfogle
That's not how it works. If it were, there would be no point to the existence of #undef. A macro is supposed to be valid between its #define and its #undef. You can see that this works fine if you have it return NAME instead of "[NAME]"
In response to Jaredfogle
Exxion is correct. We use #undef everywhere in SS13 and stringification is the only place where this behavior has been found.
In response to Jaredfogle
Preprocessor commands are parsed before any code, you're effectively defining and undefining the macro before any code is compiled at all.

This shouldn't be how it works. Preprocessor should process during the expansion phase of compilation. My guess is that this phase doesn't attempt to expand string expressions currently.

A possible workaround is to use the ancient text() proc, or just string concatenation.
In response to Jaredfogle
Yeah, was posting in a hurry. I didn't mean that's how it's supposed to work, I meant that's probably what it's doing now. I have a feeling string expansion is done a step later than it should, so the undef has already fired.
This is still an issue as of 515.1603 with the code given in the example.

/proc/foo()
#define FOO 1
#define BAR 2
switch(rand(1,2))
if(1) return "[FOO]"
if(2) return "[BAR]"
#undef FOO
#undef BAR


This works to reproduce the issue. There is no logical reason this should fail. Adding return between the undef chain and the end of the switch fixes it, for some reason.
Lummox JR resolved issue with message:
Using #undef right after a string that used embedded expressions with a #define macro could cause the macro not to be processed properly.