<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div>The model DI uses and enforces is that variables must be defined before used, and that tuple elements get independent lifetimes.  It is valid for values to be declared but never defined on some path.<br><br><div>-Chris</div></div><div><br>On Dec 1, 2013, at 8:26 AM, Dave Abrahams <<a href="mailto:dabrahams@apple.com">dabrahams@apple.com</a>> wrote:<br><br></div><blockquote type="cite"><div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div><br><br><div>Sent from my moss-covered three-handled family gradunza</div></div><div><br>On Dec 1, 2013, at 8:06 AM, Chris Lattner <<a href="mailto:clattner@apple.com">clattner@apple.com</a>> wrote:<br><br></div><blockquote type="cite"><div><meta http-equiv="Content-Type" content="text/html charset=us-ascii"><br><div><div>On Nov 30, 2013, at 10:50 PM, Dave Abrahams <<a href="mailto:dabrahams@apple.com">dabrahams@apple.com</a>> wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><div dir="auto" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div>This is probably a dumb question but did we decide that class refs default-construct to null?</div></div></blockquote><div dir="auto"><br></div><div dir="auto">No, they don't, and they can't if class references are eventually non-nullable.</div><br><blockquote type="cite"><div dir="auto" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div>  I don't remember it that way but could be mistaken.  I'd have expected DI to reject this one unless they were default constructed, in which case why should destruction be conditional?<br></div></div></blockquote><div dir="auto"><br></div><div dir="auto">Why?</div></div></div></blockquote><div><br></div>Because only one of the tuple members gets initialized<div><br><blockquote type="cite"><div><div><div dir="auto">-Chris</div><br><blockquote type="cite"><div dir="auto" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div><br>Sent from my <span style="background-color: rgba(255, 255, 255, 0);">illudium Q-36 explosive space modulator</span></div><div><br>On Nov 30, 2013, at 9:22 PM,<span class="Apple-converted-space"> </span><a href="mailto:clattner@apple.com">clattner@apple.com</a><span class="Apple-converted-space"> </span>wrote:<br><br></div><blockquote type="cite"><div id="msg"><dl class="meta"><dt>Revision</dt><dd>10701</dd><dt>Author</dt><dd>clattner</dd><dt>Date</dt><dd>2013-11-30 21:22:05 -0800 (Sat, 30 Nov 2013)</dd></dl><h3>Log Message</h3><pre>implement support for conditional destruction of tuple elements.

a testcase like this:

func test(cond : Bool) {
  var x : (SomeClass, SomeClass)

  if cond {
    x.0 = getSomeClass()
  } else {
    x.1 = getSomeClass() 
  }
}

now ends up with an epilog to destroy "x" that looks like this:

  %1 = builtin_function_ref "lshr_Int2" : $@thin @callee_owned (Builtin.Int2, Builtin.Int2) -> Builtin.Int2 // user: %37
  %2 = builtin_function_ref "trunc_Int2_Int1" : $@thin @callee_owned (Builtin.Int2) -> Builtin.Int1 // users: %38, %31
...
  %30 = load %4#1 : $*Builtin.Int2                // users: %37, %31
  %31 = apply %2(%30) : $@thin @callee_owned (Builtin.Int2) -> Builtin.Int1 // user: %32
  cond_br %31, bb4, bb5                           // id: %32

bb4:                                              // Preds: bb3
  %33 = tuple_element_addr %7#1 : $*(SomeClass, SomeClass), 0 // user: %34
  destroy_addr %33 : $*SomeClass                  // id: %34
  br bb5                                          // id: %35

bb5:                                              // Preds: bb3 bb4
  %36 = integer_literal $Builtin.Int2, 1          // user: %37
  %37 = apply %1(%30, %36) : $@thin @callee_owned (Builtin.Int2, Builtin.Int2) -> Builtin.Int2 // user: %38
  %38 = apply %2(%37) : $@thin @callee_owned (Builtin.Int2) -> Builtin.Int1 // user: %39
  cond_br %38, bb6, bb7                           // id: %39

bb6:                                              // Preds: bb5
  %40 = tuple_element_addr %7#1 : $*(SomeClass, SomeClass), 1 // user: %41
  destroy_addr %40 : $*SomeClass                  // id: %41
  br bb7                                          // id: %42

bb7:                                              // Preds: bb5 bb6
  dealloc_stack %7#0 : $*@local_storage (SomeClass, SomeClass) // id: %43</pre><h3>Modified Paths</h3><ul><li><a href="x-msg://18/#shinytrunklibSILPassesDefiniteInitializationcpp">shiny/trunk/lib/SILPasses/DefiniteInitialization.cpp</a></li><li><a href="x-msg://18/#shinytrunktestSILPassesdefinite_initializationshiny">shiny/trunk/test/SILPasses/definite_initialization.shiny</a></li></ul></div><div id="patch"><h3>Diff</h3><a id="shinytrunklibSILPassesDefiniteInitializationcpp"></a><div class="modfile"><h4>Modified: shiny/trunk/lib/SILPasses/DefiniteInitialization.cpp (10700 => 10701)</h4><pre class="diff"><span>
<span class="info">--- shiny/trunk/lib/SILPasses/DefiniteInitialization.cpp     2013-12-01 05:10:48 UTC (rev 10700)
+++ shiny/trunk/lib/SILPasses/DefiniteInitialization.cpp        2013-12-01 05:22:05 UTC (rev 10701)
</span><span class="lines">@@ -1399,6 +1399,7 @@
</span><span class="cx"> 
</span><span class="cx"> void ElementPromotion::handleConditionalDestroys(SILValue ControlVariableAddr) {
</span><span class="cx">   SILBuilder B(TheMemory);
</span><ins>+  SILValue ShiftRightFn, TruncateFn;
</ins><span class="cx">   
</span><span class="cx">   // After handling any conditional initializations, check to see if we have any
</span><span class="cx">   // cases where the value is only partially initialized by the time its
</span><span class="lines">@@ -1425,8 +1426,9 @@
</span><span class="cx">     // uninitialized, or partially initialized.  The first two cases are simple
</span><span class="cx">     // to handle, whereas the partial case requires dynamic codegen based on the
</span><span class="cx">     // liveness bitmask.
</span><del>-    for (unsigned i = 0, e = NumTupleElements; i != e; ++i) {
-      switch (Availability.get(i)) {
</del><ins>+    SILValue LoadedMask;
+    for (unsigned Elt = 0, e = NumTupleElements; Elt != e; ++Elt) {
+      switch (Availability.get(Elt)) {
</ins><span class="cx">       case DIKind::No:
</span><span class="cx">         // If an element is known to be uninitialized, then we know we can
</span><span class="cx">         // completely ignore it.
</span><span class="lines">@@ -1440,7 +1442,7 @@
</span><span class="cx">         // If an element is known to be initialized, then we can strictly
</span><span class="cx">         // destroy its value at DAI's position.
</span><span class="cx">         B.setInsertionPoint(DAI);
</span><del>-        SILValue EltPtr = computeTupleElementAddress(Addr, i, DAI->getLoc(), B);
</del><ins>+        SILValue EltPtr = computeTupleElementAddress(Addr, Elt,DAI->getLoc(),B);
</ins><span class="cx">         if (auto *DA = B.emitDestroyAddr(DAI->getLoc(), EltPtr))
</span><span class="cx">           Releases.push_back(DA);
</span><span class="cx">         continue;
</span><span class="lines">@@ -1451,20 +1453,43 @@
</span><span class="cx">       // This could be handled in processNonTrivialRelease some day.
</span><span class="cx">       
</span><span class="cx">       // Insert a load of the liveness bitmask and split the CFG into a diamond
</span><del>-      // right before it.
</del><ins>+      // right before the destroy_addr, if we haven't already loaded it.
</ins><span class="cx">       B.setInsertionPoint(DAI);
</span><del>-      auto *Load = B.createLoad(DAI->getLoc(), ControlVariableAddr);
</del><ins>+      if (!LoadedMask)
+        LoadedMask = B.createLoad(DAI->getLoc(), ControlVariableAddr);
+      SILValue CondVal = LoadedMask;
</ins><span class="cx">       
</span><del>-      // FIXME: Shift and mask the liveness down.
-      assert(NumTupleElements == 1 && "FIXME: Broken");
</del><ins>+      // If this memory object has multiple tuple elements, we need to make sure
+      // to test the right one.
+      if (NumTupleElements != 1) {
+        // Shift the mask down to this element.
+        if (Elt != 0) {
+          if (!ShiftRightFn) {
+            SILBuilder FB(TheMemory->getFunction()->begin()->begin());
+            ShiftRightFn = getBinaryFunction("lshr", CondVal.getType(),
+                                             DAI->getLoc(), FB);
+          }
+          SILValue Amt = B.createIntegerLiteral(DAI->getLoc(),
+                                                CondVal.getType(), Elt);
+          SILValue Args[] = { CondVal, Amt };
+          CondVal = B.createApply(DAI->getLoc(), ShiftRightFn, Args);
+        }
+        
+        if (!TruncateFn) {
+          SILBuilder FB(TheMemory->getFunction()->begin()->begin());
+          TruncateFn = getTruncateToI1Function(CondVal.getType(),
+                                               DAI->getLoc(), FB);
+        }
+        CondVal = B.createApply(DAI->getLoc(), TruncateFn, CondVal);
+      }
</ins><span class="cx">       
</span><span class="cx">       SILBasicBlock *CondDestroyBlock, *ContBlock;
</span><del>-      InsertCFGDiamond(SILValue(Load, 0), DAI->getLoc(),
</del><ins>+      InsertCFGDiamond(CondVal, DAI->getLoc(),
</ins><span class="cx">                        B, CondDestroyBlock, nullptr, ContBlock);
</span><span class="cx">       
</span><span class="cx">       // Set up the conditional destroy block.
</span><span class="cx">       B.setInsertionPoint(CondDestroyBlock->begin());
</span><del>-      SILValue EltPtr = computeTupleElementAddress(Addr, i, DAI->getLoc(), B);
</del><ins>+      SILValue EltPtr = computeTupleElementAddress(Addr, Elt, DAI->getLoc(), B);
</ins><span class="cx">       if (auto *DA = B.emitDestroyAddr(DAI->getLoc(), EltPtr))
</span><span class="cx">         Releases.push_back(DA);
</span><span class="cx">     }
</span></span></pre></div><a id="shinytrunktestSILPassesdefinite_initializationshiny"></a><div class="modfile"><h4>Modified: shiny/trunk/test/SILPasses/definite_initialization.shiny (10700 => 10701)</h4><pre class="diff"><span>
<span class="info">--- shiny/trunk/test/SILPasses/definite_initialization.shiny 2013-12-01 05:10:48 UTC (rev 10700)
+++ shiny/trunk/test/SILPasses/definite_initialization.shiny    2013-12-01 05:22:05 UTC (rev 10701)
</span><span class="lines">@@ -259,6 +259,16 @@
</span><span class="cx">     a.1 = 1
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+
+  func tuple_test(cond : Bool) {
+    var x : (SomeClass, SomeClass)
+
+    if cond {
+      x.0 = SomeClass()
+    } else {
+      x.1 = SomeClass()
+    }
+  }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // This tests cases where an store might be an init or assign based on control
</span><span class="lines">@@ -276,19 +286,19 @@
</span><span class="cx">   // Nontrivial type
</span><span class="cx">   var sc : SomeClass
</span><span class="cx">   if c {
</span><del>-    sc = getSomeClass()
</del><ins>+    sc = SomeClass()
</ins><span class="cx">   }
</span><del>-  sc = getSomeClass()
</del><ins>+  sc = SomeClass()
</ins><span class="cx">   
</span><span class="cx">   // Tuple element types
</span><span class="cx">   var tt : (SomeClass, SomeClass)
</span><span class="cx"> 
</span><del>-  if cond {
-    tt.0 = getSomeClass()
</del><ins>+  if c {
+    tt.0 = SomeClass()
</ins><span class="cx">   } else {
</span><del>-    tt.1 = getSomeClass()
</del><ins>+    tt.1 = SomeClass()
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  tt.0 = getSomeClass()
</del><ins>+  tt.0 = SomeClass()
</ins><span class="cx">   tt.1 = tt.0
</span><span class="cx"> }
</span></span></pre></div></div></blockquote></div></blockquote></div><br></div></blockquote></div></div></blockquote></body></html>