Repository: celery/celery

Issue:
Replaced task in chain causes chain to skip to last link (4.0.0–4.0.2)
## Checklist

- [x] I have included the output of ``celery -A proj report`` in the issue.

```
software -> celery:4.0.2 (latentcall) kombu:4.0.2 py:2.7.11
            billiard:3.5.0.2 py-amqp:2.1.4
platform -> system:Darwin arch:64bit imp:CPython
loader   -> celery.loaders.app.AppLoader
settings -> transport:amqp results:redis://localhost:6379/0

broker_url: u'amqp://guest:********@localhost:5672//'
result_backend: u'redis://localhost:6379/0'
```
(The above output is against the current `master`, at commit `8926239`.)

Also:
```
(foo) $ pip freeze | grep redis
redis==2.10.5
```

- [x] I have verified that the issue exists against the `master` branch of Celery.

## Steps to reproduce
*foo.py:*
```python
from celery import Celery
app = Celery('foo', broker='amqp://guest:guest@localhost:5672//', backend='redis://localhost:6379/0')

@app.task
def a(val):
    return val + ' A'

@app.task
def b(val):
    return val + ' B'

@app.task(bind=True)
def b_(self, val):
    raise self.replace(b.s(val))

@app.task
def c(val):
    return val + ' C'

@app.task
def d(val):
    return val + ' D'

@app.task
def e(val):
    return val + ' E'

@app.task
def f(val):
    return val + ' F'
```

*bar.py:*
```python
from foo import a, b, b_, c, d, e, f
if __name__ == '__main__':
    c1 = a.s('hello') | b.s() | c.s() | d.s() | e.s() | f.s()
    c2 = a.s('hello') | b_.s() | c.s() | d.s() | e.s() | f.s()
    c3 = a.s('hello') | b_.s() | c.s()

    print c1().get()  # hello A B C D E F
    print c2().get()  # hello A B F (expected: 'hello A B C D E F')
    print c3().get()  # hello A B C
```

## Expected behavior
All tasks in a chain containing a replaced task should be run.

## Actual behavior
Any tasks between the replaced task and the final task in the chain are not run. Only the tasks up to and including the replaced task, plus the last task in the chain, are run. Tested in 4.0.0–4.0.2 (including `master` at `8926239`).


Discussion:

Resolving patch:
diff --git a/celery/app/task.py b/celery/app/task.py
--- a/celery/app/task.py
+++ b/celery/app/task.py
@@ -850,7 +850,7 @@ def replace(self, sig):
             chord = None
 
         if self.request.chain:
-            for t in self.request.chain:
+            for t in reversed(self.request.chain):
                 sig |= signature(t, app=self.app)
 
         sig.freeze(self.request.id,
diff --git a/t/integration/tasks.py b/t/integration/tasks.py
--- a/t/integration/tasks.py
+++ b/t/integration/tasks.py
@@ -13,6 +13,12 @@ def add(x, y):
     return x + y
 
 
+@shared_task(bind=True)
+def add_replaced(self, x, y):
+    """Add two numbers (via the add task)."""
+    raise self.replace(add.s(x, y))
+
+
 @shared_task
 def print_unicode(log_message='håå®ƒ valmuefrø', print_message='hiöäüß'):
     """Task that both logs and print strings containing funny characters."""


Test patch:
diff --git a/t/integration/test_canvas.py b/t/integration/test_canvas.py
--- a/t/integration/test_canvas.py
+++ b/t/integration/test_canvas.py
@@ -4,7 +4,7 @@
 from celery.exceptions import TimeoutError
 from celery.result import AsyncResult, GroupResult
 from .conftest import flaky
-from .tasks import add, collect_ids, ids
+from .tasks import add, add_replaced, collect_ids, ids
 
 TIMEOUT = 120
 
@@ -20,12 +20,12 @@ def test_simple_chain(self, manager):
     def test_complex_chain(self, manager):
         c = (
             add.s(2, 2) | (
-                add.s(4) | add.s(8) | add.s(16)
+                add.s(4) | add_replaced.s(8) | add.s(16) | add.s(32)
             ) |
             group(add.s(i) for i in range(4))
         )
         res = c()
-        assert res.get(timeout=TIMEOUT) == [32, 33, 34, 35]
+        assert res.get(timeout=TIMEOUT) == [64, 65, 66, 67]
 
     @flaky
     def test_parent_ids(self, manager, num=10):