From 7879f56aca34d6c513019497df980064d8997371 Mon Sep 17 00:00:00 2001
From: Ketmar Dark <ketmar@ketmar.no-ip.org>
Date: Mon, 8 Aug 2016 03:47:42 +0300
Subject: [PATCH] fixed bug with dtors not called for `foreach (ref v; rng)`
 where `.front` returns non-ref

---
 src/statementsem.d                 | 66 ++++++++++++++++++++++----------------
 test/runnable/k8_ref_foreach_fix.d | 46 ++++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 28 deletions(-)
 create mode 100644 test/runnable/k8_ref_foreach_fix.d

diff --git a/src/statementsem.d b/src/statementsem.d
index 6cf5c16..aebc1f1 100644
--- a/src/statementsem.d
+++ b/src/statementsem.d
@@ -1051,12 +1051,49 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
                 e = new VarExp(loc, r);
                 Expression einit = new DotIdExp(loc, e, idfront);
                 Statement makeargs, forbody;
+
+                bool resetRef = false; // if `.front` is a function, and it doesn't return `ref`, reset `ref` in foreach arg
+
+                Type tfront;
+                if (auto fd = sfront.isFuncDeclaration())
+                {
+                    if (!fd.functionSemantic())
+                        goto Lrangeerr;
+                    tfront = fd.type;
+                }
+                else if (auto td = sfront.isTemplateDeclaration())
+                {
+                    Expressions a;
+                    if (auto f = resolveFuncCall(loc, sc, td, null, tab, &a, 1))
+                        tfront = f.type;
+                }
+                else if (auto d = sfront.isDeclaration())
+                {
+                    tfront = d.type;
+                }
+                if (!tfront || tfront.ty == Terror)
+                    goto Lrangeerr;
+                if (tfront.toBasetype().ty == Tfunction)
+                {
+                    // reset `ref` if front doesn't return ref: without this, we may miss dtor!
+                    auto ftt = cast(TypeFunction)tfront.toBasetype();
+                    resetRef = !ftt.isref;
+                    tfront = tfront.toBasetype().nextOf();
+                }
+                if (tfront.ty == Tvoid)
+                {
+                    fs.error("%s.front is void and has no value", oaggr.toChars());
+                    goto Lerror2;
+                }
+
                 if (dim == 1)
                 {
                     auto p = (*fs.parameters)[0];
                     auto ve = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, einit));
                     ve.storage_class |= STCforeach;
                     ve.storage_class |= p.storageClass & (STCin | STCout | STCref | STC_TYPECTOR);
+                    if (resetRef)
+                        ve.storage_class &= ~STCref;
 
                     makeargs = new ExpStatement(loc, ve);
                 }
@@ -1065,33 +1102,6 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
                     auto vd = copyToTemp(STCref, "__front", einit);
                     makeargs = new ExpStatement(loc, vd);
 
-                    Type tfront;
-                    if (auto fd = sfront.isFuncDeclaration())
-                    {
-                        if (!fd.functionSemantic())
-                            goto Lrangeerr;
-                        tfront = fd.type;
-                    }
-                    else if (auto td = sfront.isTemplateDeclaration())
-                    {
-                        Expressions a;
-                        if (auto f = resolveFuncCall(loc, sc, td, null, tab, &a, 1))
-                            tfront = f.type;
-                    }
-                    else if (auto d = sfront.isDeclaration())
-                    {
-                        tfront = d.type;
-                    }
-                    if (!tfront || tfront.ty == Terror)
-                        goto Lrangeerr;
-                    if (tfront.toBasetype().ty == Tfunction)
-                        tfront = tfront.toBasetype().nextOf();
-                    if (tfront.ty == Tvoid)
-                    {
-                        fs.error("%s.front is void and has no value", oaggr.toChars());
-                        goto Lerror2;
-                    }
-
                     // Resolve inout qualifier of front type
                     tfront = tfront.substWildTo(tab.mod);
 
@@ -1127,7 +1137,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
                         }
                         if (!p.type)
                             p.type = exp.type;
-                        p.type = p.type.addStorageClass(p.storageClass).semantic(loc, sc2);
+                        p.type = p.type.addStorageClass(p.storageClass & ~(resetRef ? STCref : 0L)).semantic(loc, sc2);
                         if (!exp.implicitConvTo(p.type))
                             goto Lrangeerr;
 
diff --git a/test/runnable/k8_ref_foreach_fix.d b/test/runnable/k8_ref_foreach_fix.d
new file mode 100644
index 0000000..ac346a9
--- /dev/null
+++ b/test/runnable/k8_ref_foreach_fix.d
@@ -0,0 +1,46 @@
+// PERMUTE_ARGS:
+
+int allocCount;
+
+
+struct Repeat(T) {
+  alias Type = T;
+  Type value;
+  @property empty () const { return false; }
+  void popFront() {}
+  @property ref Type front() { return value; }
+}
+
+struct Take(Range) {
+  alias Type = Range.Type;
+  Range range;
+  size_t limit;
+  @property empty () const { return !limit; }
+  void popFront () { --limit; }
+  /*ref*/ Type front () { return range.front; }
+}
+
+auto repeat(Type) (auto ref Type t) { return Repeat!Type(t); }
+
+auto take(Range) (auto ref Range range, size_t limit) { return Take!Range(range,limit); }
+
+struct Test {
+  this (int id_) { ++allocCount; }
+  this (this) { ++allocCount; }
+  ~this () { --allocCount; }
+}
+
+void testit () {
+  int count;
+  {
+    auto arr = Test(1).repeat().take(2);
+    foreach (ref v; arr) {
+      ++count;
+    }
+  }
+  if (count == 0) assert(0);
+  assert(allocCount == 0);
+}
+
+
+void main () { testit(); }
-- 
2.9.0

