Synapse generator syntax sometimes incorrect with postsynaptic condition
The following code tries to connect to a neuron with target index 10 (which does not exist), triggered by a `rand()` condition in the `if` part. It does not matter whether this is actually doing anything, e.g. `rand() <= 1.0` triggers the same problem.
```Python
source = NeuronGroup(10, '')
target = NeuronGroup(10, '')
syn = Synapses(source, target)
syn.connect(j='k for k in range(i, i+5) if i < 5 and rand() < 0.5')
```
Of course users should rather use `sample(i, i+5, p=0.5)` here, but 1) it should still work and 2) it might be a symptom of a larger issue.
diff --git a/brian2/codegen/runtime/cython_rt/templates/synapses_create_generator.pyx b/brian2/codegen/runtime/cython_rt/templates/synapses_create_generator.pyx
index 4e4f5c44..001cf718 100644
--- a/brian2/codegen/runtime/cython_rt/templates/synapses_create_generator.pyx
+++ b/brian2/codegen/runtime/cython_rt/templates/synapses_create_generator.pyx
@@ -86,21 +86,33 @@ cdef void _flush_buffer(buf, dynarr, int buf_len):
         {% endif %}
 
             {{vector_code['create_j']|autoindent}}
+            _raw_post_idx = _j + _target_offset
+
+            {% if postsynaptic_condition %}
+            {% if postsynaptic_variable_used %}
+            {# The condition could index outside of array range #}
             if _j<0 or _j>=N_post:
                 {% if skip_if_invalid %}
                 continue
                 {% else %}
                 raise IndexError("index j=%d outside allowed range from 0 to %d" % (_j, N_post-1))
                 {% endif %}
-            _raw_post_idx = _j + _target_offset
-
-            {% if postsynaptic_condition %}
+            {% endif %}
             {{vector_code['create_cond']|autoindent}}
             {% endif %}
             {% if if_expression!='True' and postsynaptic_condition %}
             if not _cond:
                 continue
             {% endif %}
+            {% if not postsynaptic_variable_used %}
+            {# Otherwise, we already checked before #}
+            if _j<0 or _j>=N_post:
+                {% if skip_if_invalid %}
+                continue
+                {% else %}
+                raise IndexError("index j=%d outside allowed range from 0 to %d" % (_j, N_post-1))
+                {% endif %}
+            {% endif %}
             {{vector_code['update_post']|autoindent}}
 
             for _repetition in range(_n):
diff --git a/brian2/codegen/runtime/numpy_rt/templates/synapses_create_generator.py_ b/brian2/codegen/runtime/numpy_rt/templates/synapses_create_generator.py_
index e5206e5a..744a51df 100644
--- a/brian2/codegen/runtime/numpy_rt/templates/synapses_create_generator.py_
+++ b/brian2/codegen/runtime/numpy_rt/templates/synapses_create_generator.py_
@@ -93,6 +93,11 @@ for _i in range(N_pre):
 
     _vectorisation_idx = {{iteration_variable}}
     {{vector_code['create_j']|autoindent}}
+    _vectorisation_idx = _j
+    _raw_post_idx = _j + _target_offset
+    {% if postsynaptic_condition %}
+    {% if postsynaptic_variable_used %}
+    {# The condition could index outside of array range #}
     {% if skip_if_invalid %}
     if _numpy.isscalar(_j):
         if _j<0 or _j>=N_post:
@@ -100,7 +105,6 @@ for _i in range(N_pre):
     else:
         _in_range = _numpy.logical_and(_j>=0, _j<N_post)
         _j = _j[_in_range]
-        {{iteration_variable}} = {{iteration_variable}}[_in_range]
     {% else %}
     if _numpy.isscalar(_j):
         _min_j = _max_j = _j
@@ -110,13 +114,11 @@ for _i in range(N_pre):
         _min_j = _numpy.min(_j)
         _max_j = _numpy.max(_j)
     if _min_j < 0:
-        raise IndexError("index j=%d outside allowed range from 0 to %d" % (_min_j, N_post-1))
+        raise IndexError("index j=%d outside allowed range from 0 to %d, and the condition refers to a post-synaptic variable" % (_min_j, N_post-1))
     elif _max_j >= N_post:
-        raise IndexError("index j=%d outside allowed range from 0 to %d" % (_max_j, N_post-1))
+        raise IndexError("index j=%d outside allowed range from 0 to %d, and the condition refers to a post-synaptic variable" % (_max_j, N_post-1))
+    {% endif %}
     {% endif %}
-    _vectorisation_idx = _j
-    _raw_post_idx = _j + _target_offset
-    {% if postsynaptic_condition %}
     {{vector_code['create_cond']|autoindent}}
     {% endif %}
     {% if if_expression!='True' and postsynaptic_condition %}
@@ -126,6 +128,28 @@ for _i in range(N_pre):
     _j, {{iteration_variable}} = _numpy.broadcast_arrays(_j, {{iteration_variable}})
     {% endif %}
 
+    {% if skip_if_invalid %}
+    if _numpy.isscalar(_j):
+        if _j<0 or _j>=N_post:
+            continue
+    else:
+        _in_range = _numpy.logical_and(_j>=0, _j<N_post)
+        _j = _j[_in_range]
+    {% elif not postsynaptic_variable_used %}
+    {# Otherwise, we already checked before #}
+    if _numpy.isscalar(_j):
+        _min_j = _max_j = _j
+    elif _j.shape == (0, ):
+        continue
+    else:
+        _min_j = _numpy.min(_j)
+        _max_j = _numpy.max(_j)
+    if _min_j < 0:
+        raise IndexError("index j=%d outside allowed range from 0 to %d" % (_min_j, N_post-1))
+    elif _max_j >= N_post:
+        raise IndexError("index j=%d outside allowed range from 0 to %d" % (_max_j, N_post-1))
+    {% endif %}
+
     _vectorisation_idx = _j
     _raw_post_idx = _j + _target_offset
     {{vector_code['update_post']|autoindent}}
diff --git a/brian2/codegen/runtime/weave_rt/templates/synapses_create_generator.cpp b/brian2/codegen/runtime/weave_rt/templates/synapses_create_generator.cpp
index f9019705..22b1762b 100644
--- a/brian2/codegen/runtime/weave_rt/templates/synapses_create_generator.cpp
+++ b/brian2/codegen/runtime/weave_rt/templates/synapses_create_generator.cpp
@@ -19,7 +19,7 @@
     const int oldsize = {{_dynamic__synaptic_pre}}.size();
 
     // scalar code
-	const int _vectorisation_idx = 1;
+    const int _vectorisation_idx = 1;
     {{scalar_code['setup_iterator']|autoindent}}
     {{scalar_code['create_j']|autoindent}}
     {{scalar_code['create_cond']|autoindent}}
@@ -97,18 +97,21 @@
             }
             _j = __j; // make the previously locally scoped _j available
             _pre_idx = __pre_idx;
-            if(_j<0 || _j>=_N_post)
-            {
-                {% if skip_if_invalid %}
-                continue;
-                {% else %}
-                PyErr_SetString(PyExc_IndexError, "index j outside allowed range");
-                throw 1;
-                {% endif %}
-            }
             _raw_post_idx = _j + _target_offset;
             {% if postsynaptic_condition %}
             {
+                {% if postsynaptic_variable_used %}
+                {# The condition could index outside of array range #}
+                if(_j<0 || _j>=_N_post)
+                {
+                    {% if skip_if_invalid %}
+                    continue;
+                    {% else %}
+                    PyErr_SetString(PyExc_IndexError, "index j outside allowed range, and the condition refers to a post-synaptic variable.");
+                    throw 1;
+                    {% endif %}
+                }
+                {% endif %}
                 {{vector_code['create_cond']|autoindent}}
                 __cond = _cond;
             }
@@ -119,6 +122,18 @@
             if(!_cond) continue;
             {% endif %}
 
+            {% if not postsynaptic_variable_used %}
+            {# Otherwise, we already checked before #}
+            if(_j<0 || _j>=_N_post)
+            {
+                {% if skip_if_invalid %}
+                continue;
+                {% else %}
+                PyErr_SetString(PyExc_IndexError, "index j outside allowed range");
+                throw 1;
+                {% endif %}
+            }
+            {% endif %}
             {{vector_code['update_post']|autoindent}}
 
             for (int _repetition=0; _repetition<_n; _repetition++) {
diff --git a/brian2/devices/cpp_standalone/templates/synapses_create_generator.cpp b/brian2/devices/cpp_standalone/templates/synapses_create_generator.cpp
index e242f857..d40a083c 100644
--- a/brian2/devices/cpp_standalone/templates/synapses_create_generator.cpp
+++ b/brian2/devices/cpp_standalone/templates/synapses_create_generator.cpp
@@ -2,9 +2,9 @@
 
 {% block maincode %}
     #include<iostream>
-	{# USES_VARIABLES { _synaptic_pre, _synaptic_post, rand,
-	                    N_incoming, N_outgoing, N,
-	                    N_pre, N_post, _source_offset, _target_offset } #}
+    {# USES_VARIABLES { _synaptic_pre, _synaptic_post, rand,
+                        N_incoming, N_outgoing, N,
+                        N_pre, N_post, _source_offset, _target_offset } #}
 
     {# WRITES_TO_READ_ONLY_VARIABLES { _synaptic_pre, _synaptic_post,
                                        N_incoming, N_outgoing, N}
@@ -24,7 +24,7 @@
     {{scalar_code['create_cond']|autoindent}}
     {{scalar_code['update_post']|autoindent}}
     for(int _i=0; _i<_N_pre; _i++)
-	{
+    {
         bool __cond, _cond;
         _raw_pre_idx = _i + _source_offset;
         {% if not postsynaptic_condition %}
@@ -97,18 +97,21 @@
             _j = __j; // make the previously locally scoped _j available
             _pre_idx = __pre_idx;
             _raw_post_idx = _j + _target_offset;
-            if(_j<0 || _j>=_N_post)
-            {
-                {% if skip_if_invalid %}
-                continue;
-                {% else %}
-                cout << "Error: tried to create synapse to neuron j=" << _j << " outside range 0 to " <<
-                        _N_post-1 << endl;
-                exit(1);
-                {% endif %}
-            }
             {% if postsynaptic_condition %}
             {
+                {% if postsynaptic_variable_used %}
+                {# The condition could index outside of array range #}
+                if(_j<0 || _j>=_N_post)
+                {
+                    {% if skip_if_invalid %}
+                    continue;
+                    {% else %}
+                    cout << "Error: tried to create synapse to neuron j=" << _j << " outside range 0 to " <<
+                                            _N_post-1 << endl;
+                    exit(1);
+                    {% endif %}
+                }
+                {% endif %}
                 {{vector_code['create_cond']|autoindent}}
                 __cond = _cond;
             }
@@ -118,7 +121,19 @@
             {% if if_expression!='True' %}
             if(!_cond) continue;
             {% endif %}
-
+            {% if not postsynaptic_variable_used %}
+            {# Otherwise, we already checked before #}
+            if(_j<0 || _j>=_N_post)
+            {
+                {% if skip_if_invalid %}
+                continue;
+                {% else %}
+                cout << "Error: tried to create synapse to neuron j=" << _j << " outside range 0 to " <<
+                        _N_post-1 << endl;
+                exit(1);
+                {% endif %}
+            }
+            {% endif %}
             {{vector_code['update_post']|autoindent}}
 
             for (int _repetition=0; _repetition<_n; _repetition++) {
diff --git a/brian2/synapses/synapses.py b/brian2/synapses/synapses.py
index 2d41dda0..bbe10bfb 100644
--- a/brian2/synapses/synapses.py
+++ b/brian2/synapses/synapses.py
@@ -1335,7 +1335,7 @@ def connect(self, condition=None, i=None, j=None, p=1., n=1,
                     j = None
                     if isinstance(p, basestring):
                         p_dep = self._expression_index_dependence(p)
-                        if '_postsynaptic_idx' in p_dep:
+                        if '_postsynaptic_idx' in p_dep or '_iterator_idx' in p_dep:
                             j = ('_k for _k in range(N_post) '
                                  'if ({expr}) and '
                                  'rand()<{p}').format(expr=condition, p=p)
@@ -1572,7 +1572,7 @@ def _expression_index_dependence(self, expr, additional_indices=None):
             if varname == 'i':
                 deps.add('_presynaptic_idx')
             elif varname == 'j':
-                deps.add('_postsynaptic_idx')
+                deps.add('_iterator_idx')
             elif varname in additional_indices:
                 deps.add(additional_indices[varname])
             else:
@@ -1618,12 +1618,17 @@ def _add_synapses_generator(self, j, n, skip_if_invalid=False, namespace=None, l
         additional_indices['_vectorisation_idx'] = '_iterator_idx'
 
         postsynaptic_condition = False
+        postsynaptic_variable_used = False
         if parsed['if_expression'] is not None:
             deps = self._expression_index_dependence(parsed['if_expression'],
                                                      additional_indices)
-            if '_postsynaptic_idx' in deps or '_iterator_idx' in deps:
+            if '_postsynaptic_idx' in deps:
+                postsynaptic_condition = True
+                postsynaptic_variable_used = True
+            elif '_iterator_idx' in deps:
                 postsynaptic_condition = True
         template_kwds['postsynaptic_condition'] = postsynaptic_condition
+        template_kwds['postsynaptic_variable_used'] = postsynaptic_variable_used
 
         abstract_code['setup_iterator'] += setupiter
         abstract_code['create_j'] += '_pre_idx = _raw_pre_idx \n'
diff --git a/brian2/tests/test_synapses.py b/brian2/tests/test_synapses.py
index c0f12dc1..af93e013 100644
--- a/brian2/tests/test_synapses.py
+++ b/brian2/tests/test_synapses.py
@@ -2112,6 +2112,42 @@ def test_synapse_generator_syntax():
     assert_raises(SyntaxError, parse_synapse_generator, 'k for k in sample(N, q=0.1)')
 
 
+def test_synapse_generator_out_of_range():
+    G = NeuronGroup(16, 'v : 1', threshold='False')
+    G.v = 'i'
+    G2 = NeuronGroup(4, 'v : 1', threshold='False')
+    G2.v = '16 + i'
+
+    S1 = Synapses(G, G2, '')
+    assert_raises(IndexError, lambda: S1.connect(j='k for k in range(0, N_post*2)'))
+
+    # This should be fine
+    S2 = Synapses(G, G, '')
+    S2.connect(j='i+k for k in range(0, 5) if i <= N_post-5')
+    expected = np.zeros((len(G), len(G)))
+    expected[np.triu_indices(len(G))] = 1
+    expected[np.triu_indices(len(G), 5)] = 0
+    expected[len(G)-4:, :] = 0
+    _compare(S2, expected)
+
+    # This should be fine (see #1037)
+    S2 = Synapses(G, G, '')
+    S2.connect(j='i+k for k in range(0, 5) if i <= N_post-5 and rand() <= 1')
+    _compare(S2, expected)
+
+    # This could in principle be fine, but we cannot test the condition without
+    # accessing the post-synaptic variable outside of its range. By analyzing
+    # the post-synaptic condition, we could find out that the value of this
+    # variable is actually irrelevant, but that makes things too complicated.
+    S3 = Synapses(G, G, '')
+    try:
+        S3.connect(j='i+k for k in range(0, 5) if i <= N_post-5 and v_post >= 0')
+        raise AssertionError('IndexError not raised')
+    except IndexError as ex:
+        # Make sure that this is actually the error raised by our template and
+        # not an exception raised by numpy
+        assert 'outside allowed range' in str(ex)
+
 @attr('standalone-compatible')
 @with_setup(teardown=reinit_devices)
 def test_synapse_generator_deterministic():
@@ -2231,6 +2267,21 @@ def test_synapse_generator_deterministic_2():
     for target in xrange(4):
         expected_converging_restricted[np.arange(4, step=2) + target * 4, target] = 1
 
+    # Connecting to post indices >= source index
+    expected_diagonal = np.zeros((len(G), len(G)), dtype=np.int32)
+    expected_diagonal[np.triu_indices(len(G))] = 1
+    S15 = Synapses(G, G)
+    S15.connect(j='i + k for k in range(0, N_post-i)')
+
+    S15b = Synapses(G, G)
+    S15b.connect(j='i + k for k in range(0, N_post)', skip_if_invalid=True)
+
+    S15c = Synapses(G, G)
+    S15c.connect(j='i + k for k in range(0, N_post) if j < N_post')
+
+    S15d = Synapses(G, G)
+    S15d.connect(j='i + k for k in range(0, N_post) if i + k < N_post')
+
     with catch_logs() as _:  # Ignore warnings about empty synapses
         run(0*ms)  # for standalone
 
@@ -2240,6 +2291,10 @@ def test_synapse_generator_deterministic_2():
     _compare(S12, expected_converging)
     _compare(S13, expected_offdiagonal)
     _compare(S14, expected_converging_restricted)
+    _compare(S15, expected_diagonal)
+    _compare(S15b, expected_diagonal)
+    _compare(S15c, expected_diagonal)
+    _compare(S15d, expected_diagonal)
 
 
 @attr('standalone-compatible')
@@ -2590,6 +2645,7 @@ def test_missing_lastupdate_error_run_regularly():
     except SkipTest:
         print('Skipping numpy-only test')
     test_synapse_generator_syntax()
+    test_synapse_generator_out_of_range()
     test_synapse_generator_deterministic()
     test_synapse_generator_deterministic_2()
     test_synapse_generator_random()
diff --git a/dev/tools/run_nose_tests_standalone.py b/dev/tools/run_nose_tests_standalone.py
index 18583886..6c1294fe 100644
--- a/dev/tools/run_nose_tests_standalone.py
+++ b/dev/tools/run_nose_tests_standalone.py
@@ -13,5 +13,6 @@
             sys.exit(1)
     else:
         if not brian2.test([], test_codegen_independent=False,
-                           test_standalone='cpp_standalone'):  # If the test fails, exit with a non-zero error code
+                           test_standalone='cpp_standalone',
+                           long_tests=True):  # If the test fails, exit with a non-zero error code
             sys.exit(1)
['brian2/codegen/runtime/numpy_rt/templates/synapses_create_generator.py_', 'brian2/tests/test_synapses.py', 'brian2/codegen/runtime/cython_rt/templates/synapses_create_generator.pyx', 'brian2/codegen/runtime/weave_rt/templates/synapses_create_generator.cpp', 'brian2/devices/cpp_standalone/templates/synapses_create_generator.cpp', 'dev/tools/run_nose_tests_standalone.py', 'brian2/synapses/synapses.py']