<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Brady asked:<div class=""><br class=""></div><div class=""><div class=""><blockquote type="cite" class="">Have you identified any situation where explicitly calling out the type in a range-based for loop has been better than using the proper form of auto?</blockquote></div><div class=""><div class=""></div></div><blockquote type="cite" class=""><div class=""><div class="">Have you identified a situation where explicitly calling out a nasty templated type, like in my example, added to readability over using auto?</div></div></blockquote><div class=""><br class=""></div><div class="">Darin asked:</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class="">I’d love to see examples where using auto substantially hurts readability so we could debate them.<br class=""></blockquote></div><div class=""><br class=""></div><div class="">In many places, I agree that auto is better. But there are a bunch of algorithms in the compiler and GC where knowing the type helps me read the code more quickly. Here are a few examples. (1) is a loop, (2) is loop-like, and (3) is a nasty templated type.</div><div class=""><br class=""></div><div class=""><b class="">1) B3 compiler code cloning loop.</b></div><div class=""><b class=""><br class=""></b></div><div class="">I find that this code is easier to read with types:</div><div class=""><b class=""><br class=""></b></div><div class=""><div class=""> Value* clone = m_proc.clone(value);</div><div class=""> for (Value*& child : clone->children()) {</div><div class=""> if (Value* newChild = mappings[i].get(child))</div><div class=""> child = newChild;</div><div class=""> }</div><div class=""> if (value->type() != Void)</div><div class=""> mappings[i].add(value, clone);</div><div class=""><br class=""></div><div class=""> cases[i]->append(clone);</div><div class=""> if (value->type() != Void)</div><div class=""> cases[i]->appendNew<UpsilonValue>(m_proc, value->origin(), clone, value);</div><div class=""><br class=""></div><div class="">Here's another code cloning loop I found - sort of the same basic algorithm:</div><div class=""><br class=""></div><div class=""><div class=""> for (Value* value : *tail) {</div><div class=""> Value* clone = m_proc.clone(value);</div><div class=""> for (Value*& child : clone->children()) {</div><div class=""> if (Value* replacement = map.get(child))</div><div class=""> child = replacement;</div><div class=""> }</div><div class=""> if (value->type() != Void)</div><div class=""> map.add(value, clone);</div><div class=""> block->append(clone);</div><div class=""> }</div></div><div class="" style="font-weight: bold;"><br class=""></div><div class="">When reading this code, it's pretty important to know that value, clone, child, newChild, and replacement are all Values. As soon as you know this piece of information the algorithm - its purpose and how it functions - becomes clear. You can infer this information if you know where to look - m_proc.clone() and clone->children() are give-aways - but this isn't as immediately obvious as the use of the type name.</div><div class=""><br class=""></div><div class="">If someone refactored this code to use auto, it would be harder for me to read this code. I would spend more time reading it than I would have spent if it spelled out the type. I like seeing the type spelled out because that's how I recognize if the loop is over blocks, values, or something else. I like to spell out the type even when it's super obvious:</div><div class=""><br class=""></div><div class=""><div class=""> for (BasicBlock* block : m_proc) {</div><div class=""> for (Value* value : *block) {</div><div class=""> if (value->opcode() == Phi && candidates.contains(block))</div><div class=""> valuesToDemote.add(value);</div><div class=""> for (Value* child : value->children()) {</div><div class=""> if (child->owner != block && candidates.contains(child->owner))</div><div class=""> valuesToDemote.add(child);</div><div class=""> }</div><div class=""> }</div><div class=""> }</div></div><div class=""><br class=""></div><div class="">Sticking to this format for compiler loops means that I spend less time reading the code, because I can recognize important patterns at a glance.</div><div class=""><br class=""></div><div class=""><b class="">2) Various GC loops</b></div><div class=""><b class=""><br class=""></b></div><div class="">The GC usually loops using lambdas, but the same question comes into play: is the value's type auto or is it spelled out?</div><div class=""><br class=""></div><div class=""><div class=""> forEachFreeCell(</div><div class=""> freeList,</div><div class=""> [&] (HeapCell* cell) {</div><div class=""> if (false)</div><div class=""> dataLog("Free cell: ", RawPointer(cell), "\n");</div><div class=""> if (m_attributes.destruction == NeedsDestruction)</div><div class=""> cell->zap();</div><div class=""> clearNewlyAllocated(cell);</div><div class=""> });</div></div><div class=""><br class=""></div><div class="">It's useful to know that 'cell' is a HeapCell, not a FreeCell or a JSCell. I believe that this code will only compile if you say HeapCell. Combined with the function name, this tells you that this is a cell that is free, but not necessarily on a free-list ('cause then it would be a FreeCell). This also tells you that the cell wasn't necessarily a JSCell before it was freed - it could have been a raw backing store. That's important because then we don't have a guarantee about the format of its header.</div><div class=""><br class=""></div><div class="">I think that spelling out the type really helps here. In the GC, we often assert everything, everywhere, all the time. Typical review comes back with suggestions for more assertions. Types are a form of assertion, so they are consistent with how we hack the GC.</div><div class=""><br class=""></div><div class=""><b class="">3) AirEmitShuffle</b></div><div class=""><b class=""><br class=""></b></div><div class="">My least favorite part of compilers is the shuffle. That's the algorithm that figures out how to move data from one set of registers to another, where the sets may overlap.</div><div class=""><br class=""></div><div class="">It has code like this:</div><div class=""><br class=""></div><div class=""><div class=""> while (Arg src = worklist.pop()) {</div><div class=""> HashMap<Arg, Vector<ShufflePair>>::iterator iter = mapping.find(src);</div><div class=""> if (iter == mapping.end()) {</div><div class=""> // With a shift it's possible that we previously built the tail of this shift.</div><div class=""> // See if that's the case now.</div><div class=""> if (verbose)</div><div class=""> dataLog("Trying to append shift at ", src, "\n");</div><div class=""> currentPairs.appendVector(shifts.take(src));</div><div class=""> continue;</div><div class=""> }</div><div class=""> Vector<ShufflePair> pairs = WTFMove(iter->value);</div><div class=""> mapping.remove(iter);</div><div class=""><br class=""></div><div class=""> for (const ShufflePair& pair : pairs) {</div><div class=""> currentPairs.append(pair);</div><div class=""> ASSERT(pair.src() == src);</div><div class=""> worklist.push(pair.dst());</div><div class=""> }</div><div class=""> }</div></div><div class=""><br class=""></div><div class="">I sure appreciate the type of that iterator. I totally agree that in 99% of cases, the type of an iterator should be auto because you really don't want to know its type.</div><div class=""><br class=""></div><div class="">But in this code, seeing the type of that iterator makes it much easier to tell at a glance what the code is doing. Without that type, I'd have to scroll around to see what mapping really is. It's great to know that the HashMap maps Args to Vector<ShufflePair>, since this reassures me that there are no fancy conversions when src is used as a key and when the iter->value is WTFMoved to my pairs variable.</div><div class=""><br class=""></div><div class="">Oh, and if the while loop used auto instead of Arg, I'd be super confused. In the compiler, I think that loops that benefit from explicit type far outnumber loops that don't.</div><div class=""><br class=""></div><div class="">-Filip</div></div></div></body></html>