Repository: cython/cython

Issue:
Compile time DEF expression inside IF/ELIF/ELSE statement
I am trying to use processor's `DEF` inside `IF`. I have asked this question [here](https://stackoverflow.com/questions/45156510/preprocessor-def-can-not-be-conditioned-inside-if-elif-and-else-statement-in-cy) as well. The equivalent code in C of what I am trying to write is:

```
#define A 1
#if A == 1
    #define B 2
#else
    #define B 3
#endif
```
In above `B` should be `2`, whereas it gives `3` if I write it in Cython. Here is a sample:

```
# in Definitions.pxi
DEF A=1
IF A==1:
    DEF B=2
    print('B should be 2.')
ELSE:
    DEF B=3
    print('B should be 3.')
```
and 
```
# In test.pyx
include "Definitions.pxi"
print('B is: %d'%B)
```
and
```
# In setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
setup(name = "test",ext_modules = cythonize(Extension("*",["test.pyx"])))
```
Now, testing it:
```
>>> ipython
In [1]: %reload_ext Cython
In [2]: import test

B should be 2
B is: 3
```
It seems that the very last `DEF` statement is being used and all `IF` as well as `ELIF` and `ELSE` are ignored.  I am wondering if this is a bug or I am not using it correctly.

Thanks.
Compile time DEF expression inside IF/ELIF/ELSE statement
I am trying to use processor's `DEF` inside `IF`. I have asked this question [here](https://stackoverflow.com/questions/45156510/preprocessor-def-can-not-be-conditioned-inside-if-elif-and-else-statement-in-cy) as well. The equivalent code in C of what I am trying to write is:

```
#define A 1
#if A == 1
    #define B 2
#else
    #define B 3
#endif
```
In above `B` should be `2`, whereas it gives `3` if I write it in Cython. Here is a sample:

```
# in Definitions.pxi
DEF A=1
IF A==1:
    DEF B=2
    print('B should be 2.')
ELSE:
    DEF B=3
    print('B should be 3.')
```
and 
```
# In test.pyx
include "Definitions.pxi"
print('B is: %d'%B)
```
and
```
# In setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
setup(name = "test",ext_modules = cythonize(Extension("*",["test.pyx"])))
```
Now, testing it:
```
>>> ipython
In [1]: %reload_ext Cython
In [2]: import test

B should be 2
B is: 3
```
It seems that the very last `DEF` statement is being used and all `IF` as well as `ELIF` and `ELSE` are ignored.  I am wondering if this is a bug or I am not using it correctly.

Thanks.


Discussion:
Interesting case. Semantically, the code you wrote does not mean what you intended. Instead, ``IF`` means: if the condition is true, then include its *code block* in the compilation. By design, ``DEF`` does not belong into the code block, because it is *evaluated* at compile time, not *included* at compile time. ``DEF`` is a compile time thing and not controlled by conditions itself. Meaning, both ``DEF`` assignments are being evaluated, in code order, and the last one survives and goes into the execution of the ``print()``.

Now, the fix could go either way: either implement a control flow for ``DEF`` inside of conditionals, or disallow indented ``DEF``s. I'm slightly leaning towards the latter since it's not really clear to me that the first is a feature worth having. These compile time expressions have a tendency to be generally overused...
Thanks for your response. I see the difference now. However it seems the documentation [here](http://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#conditional-statements) is not giving this detail. I am quoting

> An IF statement can appear anywhere that a normal statement or declaration can appear, and it can contain any statements or declarations that would be valid in that context, including DEF statements and other IF statements.

I am actually using this for early-binding. User at compile time can set which classes to be compiled.
Interesting case. Semantically, the code you wrote does not mean what you intended. Instead, ``IF`` means: if the condition is true, then include its *code block* in the compilation. By design, ``DEF`` does not belong into the code block, because it is *evaluated* at compile time, not *included* at compile time. ``DEF`` is a compile time thing and not controlled by conditions itself. Meaning, both ``DEF`` assignments are being evaluated, in code order, and the last one survives and goes into the execution of the ``print()``.

Now, the fix could go either way: either implement a control flow for ``DEF`` inside of conditionals, or disallow indented ``DEF``s. I'm slightly leaning towards the latter since it's not really clear to me that the first is a feature worth having. These compile time expressions have a tendency to be generally overused...
Thanks for your response. I see the difference now. However it seems the documentation [here](http://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#conditional-statements) is not giving this detail. I am quoting

> An IF statement can appear anywhere that a normal statement or declaration can appear, and it can contain any statements or declarations that would be valid in that context, including DEF statements and other IF statements.

I am actually using this for early-binding. User at compile time can set which classes to be compiled.

Resolving patch:
diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py
--- a/Cython/Compiler/Parsing.py
+++ b/Cython/Compiler/Parsing.py
@@ -2160,9 +2160,10 @@ def p_DEF_statement(s):
     name = p_ident(s)
     s.expect('=')
     expr = p_compile_time_expr(s)
-    value = expr.compile_time_value(denv)
-    #print "p_DEF_statement: %s = %r" % (name, value) ###
-    denv.declare(name, value)
+    if s.compile_time_eval:
+        value = expr.compile_time_value(denv)
+        #print "p_DEF_statement: %s = %r" % (name, value) ###
+        denv.declare(name, value)
     s.expect_newline("Expected a newline", ignore_semicolon=True)
     return Nodes.PassStatNode(pos)
 


Test patch:
diff --git a/tests/run/ct_IF.pyx b/tests/run/ct_IF.pyx
--- a/tests/run/ct_IF.pyx
+++ b/tests/run/ct_IF.pyx
@@ -42,3 +42,33 @@ def h():
     ELSE:
         i = 3
     return i
+
+
+def control_flow_DEF1():
+    """
+    >>> control_flow_DEF1()
+    B should be 2.
+    2
+    """
+    IF YES:
+        DEF B=2
+        print('B should be 2.')
+    ELSE:
+        DEF B=3
+        print('B should be 3.')
+    return B
+
+
+def control_flow_DEF2():
+    """
+    >>> control_flow_DEF2()
+    B should be 3.
+    3
+    """
+    IF NO:
+        DEF B=2
+        print('B should be 2.')
+    ELSE:
+        DEF B=3
+        print('B should be 3.')
+    return B