<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <link rel='alternate' type='text/html' href='' />
  <link rel='self' type='application/atom+xml' href='' />
  <id></id>
  <updated>2020-05-14T12:35:41Z</updated>
  <author>
    <name></name>
    <uri></uri>
  </author>

  <entry>
    <title>Entry 8: Incomplete Computed Properties</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2020-05-14T12:35:04Z</published>
    <updated>2020-05-14T12:35:04Z</updated>
    <content type='html'><![CDATA[
    <p>It's been a long time since I managed to land a change to the Swift compiler. (Has it really been almost 2 years?!) Being a dad, changing jobs, other projects, and the difficulty (for me) of working in C++ have all keep me away (although I have tried on a few occasions). But I'm glad to have finally made another contribution.</p>

<p>When I'm working in Swift, I try to make note of confusing errors that I or others run into. When I have time to work on the Swift compiler, these are the things that I try to improve.</p>

<p>Lately, I've been bothered by this one:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">struct</span> <span class="nc">S</span> <span class="p">{}</span>

<span class="kd">extension</span> <span class="nc">S</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nv">foo</span><span class="p">:</span> <span class="nb">Int</span> <span class="p">{</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>I've written a computed property in an extension, but haven't filled out the implementation. I do this frequently. I decide that I need a property, write its declaration, and then pause to plan my implementation.</p>

<p>But at this point, Swift gives me <strong>2</strong> errors:</p>

<pre><code>example.swift:5:5: error: computed property must have accessors specified
    }
    ^
example.swift:4:9: error: extensions must not contain stored properties
    var foo: Int {
        ^
</code></pre>

<p>And not only that, but:</p>

<ol>
<li>They appear at separate points in the declaration</li>
<li>One says the property is computed and the other says that it's stored</li>
</ol>

<p>So I decided I'd try to improve this by removing the 2nd error.</p>

<p>My first step was to find the source of the errors. This is pretty easy, since you can work backwards from the error message:</p>

<ol>
<li>Search the project for a string from the error, recognizing that parts of the message can be variable.</li>
<li>Once you've found the error string in a <code>.def</code> file, search for the name of the diagnostic.</li>
<li>Set breakpoints where the diagnostic is emitted.</li>
<li>Run the code again to see where exactly the diagnostics are emitted in this case.</li>
</ol>

<p>In this case, the computed property error <a href="https://github.com/apple/swift/blob/b121ce958091aff1b0fbea8f5873d59b81f16c34/lib/Parse/ParseDecl.cpp#L5451">comes from the parser</a> and the store property error <a href="https://github.com/apple/swift/blob/b121ce958091aff1b0fbea8f5873d59b81f16c34/lib/Sema/TypeCheckStorage.cpp#L2941">comes from the typechecker</a>. For the latter error, the typechecker emits the error if (1) the property is an an extension and (2) it has storage—i.e. exactly what the error says.</p>

<p>To prevent this, I decided to alter the parser to treat an empty set of braces as a getter—instead of complaining about the getter and marking it as a setter. I copied-and-pasted some code to add a getter and verified that it would fix the issue.</p>

<p>Then I had to make sure that I didn’t break any other diagnostics. In particular, this protocol diagnostic depends on the existing storage behavior:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">protocol</span> <span class="nc">ProtocolGetSet2</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nv">a</span><span class="p">:</span> <span class="nb">Int</span> <span class="p">{}</span> <span class="c1">// expected-error {{property in protocol must have explicit { get } or { get set } specifier}} {{14-16={ get &lt;#set#&gt; \}}}</span>
<span class="p">}</span>
</code></pre></div>

<p>But there were also some other cases of <code>computed property must have accessors specified</code> that I didn’t want to break. I ran the test suite to find some examples, then decided to add the getter only when in an extension:</p>

<div class="codehilite"><pre><span></span><code>     <span class="c1">// In an extension, add a getter to prevent an &quot;extensions must not contain</span>
     <span class="c1">// stored properties&quot; error.</span>
     <span class="k">if</span> <span class="p">(</span><span class="n">Flags</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="n">PD_InExtension</span><span class="p">))</span> <span class="p">{</span>
       <span class="k">auto</span> <span class="n">getter</span> <span class="o">=</span> <span class="n">createAccessorFunc</span><span class="p">(</span>
           <span class="n">Tok</span><span class="p">.</span><span class="n">getLoc</span><span class="p">(),</span> <span class="cm">/*ValueNamePattern*/</span> <span class="k">nullptr</span><span class="p">,</span> <span class="n">GenericParams</span><span class="p">,</span> <span class="n">Indices</span><span class="p">,</span>
           <span class="n">StaticLoc</span><span class="p">,</span> <span class="n">Flags</span><span class="p">,</span> <span class="n">AccessorKind</span><span class="o">::</span><span class="n">Get</span><span class="p">,</span> <span class="n">storage</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span>
           <span class="cm">/*AccessorKeywordLoc*/</span> <span class="n">SourceLoc</span><span class="p">());</span>
       <span class="n">accessors</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">getter</span><span class="p">);</span>
     <span class="p">}</span>
</code></pre></div>

<p>At this point, I ran the tests and <a href="https://github.com/apple/swift/pull/31508">opened a PR</a>.</p>

<p>On the PR, I received feedback that it’d be best to tackle this a different way: instead of adding a fake getter in the Parser, adjust the storage request (<code>StorageImplInfoRequest::evaluate</code>) to return that there’s a getter.</p>

<p>So I reverted my fix, changed the default in this method to return <code>Get</code>, and re-ran my tests. It did fix the 2nd diagnostic, but it also broke other tests (as expected), so this was a workable solution.</p>

<p>I added another <code>if</code> to this method to check whether (1) this was in an extension and (2) had braces, and returned <code>Get</code> in this case.</p>

<div class="codehilite"><pre><span></span><code>   <span class="c1">// Extensions can&#39;t have stored properties. If there are braces, assume</span>
   <span class="c1">// this is an incomplete computed property. This avoids an &quot;extensions</span>
   <span class="c1">// must not contain stored properties&quot; error later on.</span>
   <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">isa</span><span class="o">&lt;</span><span class="n">ExtensionDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">storage</span><span class="o">-&gt;</span><span class="n">getDeclContext</span><span class="p">())</span> <span class="o">&amp;&amp;</span>
              <span class="n">storage</span><span class="o">-&gt;</span><span class="n">getBracesRange</span><span class="p">().</span><span class="n">isValid</span><span class="p">())</span> <span class="p">{</span>
     <span class="n">readImpl</span> <span class="o">=</span> <span class="n">ReadImplKind</span><span class="o">::</span><span class="n">Get</span><span class="p">;</span>
</code></pre></div>

<p>And that worked!</p>

<p>As usual, this involved a lot of fumbling around. But by choosing something small, persevering, and receiving PR feedback, I was able to contribute a fix for a diagnostic issue that plagues me almost daily. This was humbling, educational, and an investment in my day-to-day happiness.</p>

<p>The PR is <a href="https://github.com/apple/swift/pull/31508">here</a>.</p>

    ]]></content>
  </entry>

  <entry>
    <title>Entry 7: Member Operator Errors</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2018-08-31T13:29:44Z</published>
    <updated>2018-08-31T13:29:44Z</updated>
    <content type='html'><![CDATA[
    <p>Swift’s synthesized <code>Equatable</code> and <code>Hashable</code> conformance (<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md">SE-0185</a>) is one of my favorite Swift features. I use a lot of value types and use equality extensively for testing.</p>

<p>But there’s one part I don’t love: the errors if one of the types you rely on don’t conform to the requisite protocol.</p>

<div class="codehilite"><pre><span></span><code><span class="kd">struct</span> <span class="nc">NotEquatable</span> <span class="p">{}</span>

<span class="kd">struct</span> <span class="nc">B</span><span class="p">:</span> <span class="nb">Equatable</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nv">a</span><span class="p">:</span> <span class="n">NotEquatable</span>
<span class="p">}</span>
</code></pre></div>

<p>Swift doesn’t give you any hints about why it can’t synthesize a <code>==</code> for you. But worse still: it notes every <code>==</code> in the standard library (64 of them!):</p>

<pre><code>error: type 'B' does not conform to protocol 'Equatable'
struct B: Equatable {
       ^
Swift.==:1:13: note: candidate has non-matching type '(Any.Type?, Any.Type?) -&gt; Bool'
public func == (t0: Any.Type?, t1: Any.Type?) -&gt; Bool
            ^
Swift.==:1:13: note: candidate has non-matching type '&lt;T where T : RawRepresentable, T.RawValue : Equatable&gt; (T, T) -&gt; Bool'
public func == &lt;T&gt;(lhs: T, rhs: T) -&gt; Bool where T : RawRepresentable, T.RawValue : Equatable
            ^
Swift.==:1:13: note: candidate has non-matching type '((), ()) -&gt; Bool'
public func == (lhs: (), rhs: ()) -&gt; Bool
            ^
Swift.==:1:13: note: candidate has non-matching type '&lt;A, B where A : Equatable, B : Equatable&gt; ((A, B), (A, B)) -&gt; Bool'
…and so on
</code></pre>

<p>I ran into recently at work with some large types and realized that Swift could and should provide something better. I’d like for Swift to say <code>note: cannot synthesize `==` because `NotEquatable` is not `Equatable`</code>. But before doing that, I thought this more general problem should be addressed. (More diagnostics won’t help if they’re lost in the see of <code>non-matching type</code> diagnostics.)</p>

<p>So I jumped in by looking for the source of the existing error. Searching for <code>non-matching</code> in <code>DiagnosticsSema.def</code> quickly turned up the relevant diagnostic. Searching for its ID turned up one match in <code>TypeCheckProtocol.cpp:diagnoseMatch</code>. It wasn’t obvious where this function was getting called from, so I set a breakpoint and ran my test script.</p>

<p>It was called in a loop inside <code>ConformanceChecker::resolveWitnessViaLookup</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="c1">// Diagnose each of the matches.</span>
<span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="k">auto</span> <span class="o">&amp;</span><span class="nl">match</span> <span class="p">:</span> <span class="n">matches</span><span class="p">)</span>
    <span class="n">diagnoseMatch</span><span class="p">(</span><span class="n">dc</span><span class="o">-&gt;</span><span class="n">getParentModule</span><span class="p">(),</span> <span class="n">conformance</span><span class="p">,</span> <span class="n">requirement</span><span class="p">,</span> <span class="n">match</span><span class="p">);</span>
</code></pre></div>

<p>Those matches were found earlier in the function:</p>

<div class="codehilite"><pre><span></span><code><span class="c1">// Find the best witness for the requirement.</span>
<span class="n">SmallVector</span><span class="o">&lt;</span><span class="n">RequirementMatch</span><span class="p">,</span> <span class="mi">4</span><span class="o">&gt;</span> <span class="n">matches</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="n">numViable</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="n">bestIdx</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">bool</span> <span class="n">doNotDiagnoseMatches</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="kt">bool</span> <span class="n">ignoringNames</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="kt">bool</span> <span class="n">considerRenames</span> <span class="o">=</span>
    <span class="o">!</span><span class="n">canDerive</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">requirement</span><span class="o">-&gt;</span><span class="n">getAttrs</span><span class="p">().</span><span class="n">hasAttribute</span><span class="o">&lt;</span><span class="n">OptionalAttr</span><span class="o">&gt;</span><span class="p">()</span> <span class="o">&amp;&amp;</span>
    <span class="o">!</span><span class="n">requirement</span><span class="o">-&gt;</span><span class="n">getAttrs</span><span class="p">().</span><span class="n">isUnavailable</span><span class="p">(</span><span class="n">TC</span><span class="p">.</span><span class="n">Context</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">findBestWitness</span><span class="p">(</span><span class="n">requirement</span><span class="p">,</span>
                  <span class="n">considerRenames</span> <span class="o">?</span> <span class="o">&amp;</span><span class="nl">ignoringNames</span> <span class="p">:</span> <span class="k">nullptr</span><span class="p">,</span>
                  <span class="n">Conformance</span><span class="p">,</span>
                  <span class="cm">/* out parameters: */</span>
                  <span class="n">matches</span><span class="p">,</span> <span class="n">numViable</span><span class="p">,</span> <span class="n">bestIdx</span><span class="p">,</span> <span class="n">doNotDiagnoseMatches</span><span class="p">))</span> <span class="p">{</span>
</code></pre></div>

<p>Since this is C++, <code>matches</code> is returned via an out parameter, which is nicely labeled here. Placing an earlier breakpoint and stepping into <code>findBestWitness</code> led me to <code>WitnessChecker::lookupValueWitnesses</code>, which is responsible for looking up possible implementations for a given protocol requirement.</p>

<p>It, in turn, had code to look up and return the witness for operators:</p>

<div class="codehilite"><pre><span></span><code><span class="k">if</span> <span class="p">(</span><span class="n">req</span><span class="o">-&gt;</span><span class="n">isOperator</span><span class="p">())</span> <span class="p">{</span>
    <span class="c1">// Operator lookup is always global.</span>
    <span class="k">auto</span> <span class="n">lookupOptions</span> <span class="o">=</span> <span class="n">defaultUnqualifiedLookupOptions</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">DC</span><span class="o">-&gt;</span><span class="n">isCascadingContextForLookup</span><span class="p">(</span><span class="nb">false</span><span class="p">))</span>
        <span class="n">lookupOptions</span> <span class="o">|=</span> <span class="n">NameLookupFlags</span><span class="o">::</span><span class="n">KnownPrivate</span><span class="p">;</span>
    <span class="k">auto</span> <span class="n">lookup</span> <span class="o">=</span> <span class="n">TC</span><span class="p">.</span><span class="n">lookupUnqualified</span><span class="p">(</span><span class="n">DC</span><span class="o">-&gt;</span><span class="n">getModuleScopeContext</span><span class="p">(),</span>
                                       <span class="n">req</span><span class="o">-&gt;</span><span class="n">getBaseName</span><span class="p">(),</span>
                                       <span class="n">SourceLoc</span><span class="p">(),</span>
                                       <span class="n">lookupOptions</span><span class="p">);</span>
    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">candidate</span> <span class="p">:</span> <span class="n">lookup</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">witnesses</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">decl</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>If I could avoid adding irrelevant <code>decl</code>s to <code>witnesses</code>, then I would see fewer diagnostics. So how could I exclude <code>decl</code>s?</p>

<p>Luckily, I remembered that protocols can only have operators if one of the operands is <code>Self</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">protocol</span> <span class="nc">B</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="kd">func</span> <span class="o">+</span><span class="p">(</span><span class="n">lhs</span><span class="p">:</span> <span class="nb">Int</span><span class="p">,</span> <span class="n">rhs</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">String</span>
<span class="p">}</span>

<span class="n">error</span><span class="p">:</span> <span class="n">member</span> <span class="kd">operator</span> <span class="err">&#39;</span><span class="o">+</span><span class="err">&#39;</span> <span class="n">of</span> <span class="kd">protocol</span> <span class="err">&#39;</span><span class="n">B</span><span class="err">&#39;</span> <span class="n">must</span> <span class="n">have</span> <span class="n">at</span> <span class="n">least</span> <span class="n">one</span> <span class="n">argument</span> <span class="n">of</span> <span class="n">type</span> <span class="err">&#39;</span><span class="kc">Self</span><span class="err">&#39;</span>
        <span class="kd">static</span> <span class="kd">func</span> <span class="o">+</span><span class="p">(</span><span class="n">lhs</span><span class="p">:</span> <span class="nb">Int</span><span class="p">,</span> <span class="n">rhs</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">String</span>
                    <span class="o">^</span>
</code></pre></div>

<p>That seemed like a good heuristic to use. Most of the <code>==</code>s I wanted to exclude didn’t use type I was trying to conform to <code>Equatable</code>—which is why they were so bothersome.</p>

<p>So I did what any reasonable programmer would do: I stole code from the source of that error. After looking it up, I found this code in <code>TypeCheckDecl.cpp</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="kt">void</span> <span class="nf">checkMemberOperator</span><span class="p">(</span><span class="n">TypeChecker</span> <span class="o">&amp;</span><span class="n">TC</span><span class="p">,</span> <span class="n">FuncDecl</span> <span class="o">*</span><span class="n">FD</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Check that member operators reference the type of &#39;Self&#39;.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">FD</span><span class="o">-&gt;</span><span class="n">isInvalid</span><span class="p">())</span> <span class="k">return</span><span class="p">;</span>

  <span class="k">auto</span> <span class="o">*</span><span class="n">DC</span> <span class="o">=</span> <span class="n">FD</span><span class="o">-&gt;</span><span class="n">getDeclContext</span><span class="p">();</span>
  <span class="k">auto</span> <span class="n">selfNominal</span> <span class="o">=</span> <span class="n">DC</span><span class="o">-&gt;</span><span class="n">getSelfNominalTypeDecl</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">selfNominal</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>

  <span class="c1">// Check the parameters for a reference to &#39;Self&#39;.</span>
  <span class="kt">bool</span> <span class="n">isProtocol</span> <span class="o">=</span> <span class="n">isa</span><span class="o">&lt;</span><span class="n">ProtocolDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">selfNominal</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">param</span> <span class="p">:</span> <span class="o">*</span><span class="n">FD</span><span class="o">-&gt;</span><span class="n">getParameters</span><span class="p">())</span> <span class="p">{</span>
    <span class="k">auto</span> <span class="n">paramType</span> <span class="o">=</span> <span class="n">param</span><span class="o">-&gt;</span><span class="n">getInterfaceType</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">paramType</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>

    <span class="c1">// Look through a metatype reference, if there is one.</span>
    <span class="n">paramType</span> <span class="o">=</span> <span class="n">paramType</span><span class="o">-&gt;</span><span class="n">getMetatypeInstanceType</span><span class="p">();</span>

    <span class="c1">// Is it the same nominal type?</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">paramType</span><span class="o">-&gt;</span><span class="n">getAnyNominal</span><span class="p">()</span> <span class="o">==</span> <span class="n">selfNominal</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">isProtocol</span><span class="p">)</span> <span class="p">{</span>
      <span class="c1">// For a protocol, is it the &#39;Self&#39; type parameter?</span>
      <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">genericParam</span> <span class="o">=</span> <span class="n">paramType</span><span class="o">-&gt;</span><span class="n">getAs</span><span class="o">&lt;</span><span class="n">GenericTypeParamType</span><span class="o">&gt;</span><span class="p">())</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">genericParam</span><span class="o">-&gt;</span><span class="n">isEqual</span><span class="p">(</span><span class="n">DC</span><span class="o">-&gt;</span><span class="n">getSelfInterfaceType</span><span class="p">()))</span>
          <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// We did not find &#39;Self&#39;. Complain.</span>
  <span class="n">TC</span><span class="p">.</span><span class="n">diagnose</span><span class="p">(</span><span class="n">FD</span><span class="p">,</span> <span class="n">diag</span><span class="o">::</span><span class="n">operator_in_unrelated_type</span><span class="p">,</span>
              <span class="n">FD</span><span class="o">-&gt;</span><span class="n">getDeclContext</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getDeclaredInterfaceType</span><span class="p">(),</span>
              <span class="n">isProtocol</span><span class="p">,</span> <span class="n">FD</span><span class="o">-&gt;</span><span class="n">getFullName</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div>

<p>I copied this over, removed parts I didn’t think I’d need, and changed it to return a <code>bool</code> to say whether it was valid. I then called it in the loop before adding each <code>decl</code> to <code>witnesses</code>.</p>

<div class="codehilite"><pre><span></span><code><span class="kt">bool</span> <span class="n">WitnessChecker</span><span class="o">::</span><span class="n">isMemberOperator</span><span class="p">(</span><span class="n">FuncDecl</span> <span class="o">*</span><span class="n">decl</span><span class="p">,</span> <span class="n">Type</span> <span class="n">type</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">auto</span> <span class="n">nominal</span> <span class="o">=</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">getAnyNominal</span><span class="p">();</span>

  <span class="c1">// Check the parameters for a reference to &#39;Self&#39;.</span>
  <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">param</span> <span class="p">:</span> <span class="o">*</span><span class="n">decl</span><span class="o">-&gt;</span><span class="n">getParameters</span><span class="p">())</span> <span class="p">{</span>
    <span class="k">auto</span> <span class="n">paramType</span> <span class="o">=</span> <span class="n">param</span><span class="o">-&gt;</span><span class="n">getInterfaceType</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">paramType</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>

    <span class="c1">// Look through a metatype reference, if there is one.</span>
    <span class="n">paramType</span> <span class="o">=</span> <span class="n">paramType</span><span class="o">-&gt;</span><span class="n">getMetatypeInstanceType</span><span class="p">();</span>

    <span class="c1">// Is it the same nominal type?</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">paramType</span><span class="o">-&gt;</span><span class="n">getAnyNominal</span><span class="p">()</span> <span class="o">==</span> <span class="n">nominal</span><span class="p">)</span>
      <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>

<p>I ran the changes on my test file and it seemed to work! Yay! So now I attempted to run the full test suite with <code>utils/build-script --debug --test</code>.</p>

<p>Unfortunately, that didn’t work: the standard library didn’t compile. Oops. I definitely broke something. But what? It’s hard to diagnose failures in the standard library—particularly when they involve <code>.gyb</code> files.</p>

<p>To work around this, I:</p>

<ol>
<li>Stashed my changes</li>
<li>Built with <code>utils/build-script</code> to rebuild the standard library</li>
<li>Unstashed my changes</li>
<li>Built just <code>swift</code> with <code>ninja -C path/to/build/dir swift</code></li>
<li>Ran the tests with <code>lit</code></li>
</ol>

<p>That uncovered a bunch of test failures to look through. One case I hadn’t considered was operators in protocol extensions:</p>

<div class="codehilite"><pre><span></span><code><span class="kr">infix</span> <span class="kd">operator</span> <span class="o">%%%</span>
<span class="kr">infix</span> <span class="kd">operator</span> <span class="o">%%%%</span>

<span class="kd">protocol</span> <span class="nc">P1</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="kd">func</span> <span class="o">%%%</span><span class="p">(</span><span class="n">lhs</span><span class="p">:</span> <span class="kc">Self</span><span class="p">,</span> <span class="n">rhs</span><span class="p">:</span> <span class="kc">Self</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Bool</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="nc">P1</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="kd">func</span> <span class="o">%%%%</span><span class="p">(</span><span class="n">lhs</span><span class="p">:</span> <span class="kc">Self</span><span class="p">,</span> <span class="n">rhs</span><span class="p">:</span> <span class="kc">Self</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Bool</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">!</span><span class="p">(</span><span class="n">lhs</span> <span class="o">%%%</span> <span class="n">rhs</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>The check for <code>Self</code> that I’d removed was still needed.</p>

<p>Another was when the operator was generic:</p>

<div class="codehilite"><pre><span></span><code><span class="kr">infix</span> <span class="kd">operator</span> <span class="o">&lt;~&gt;</span>
<span class="kd">protocol</span> <span class="nc">P1</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="kd">func</span> <span class="o">&lt;~&gt;</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="kc">Self</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="kc">Self</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">protocol</span> <span class="nc">P2</span> <span class="p">{}</span>

<span class="kd">struct</span> <span class="nc">ConformsWithMoreGenericWitnesses</span> <span class="p">:</span> <span class="n">P1</span><span class="p">,</span> <span class="n">P2</span> <span class="p">{</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="o">&lt;~&gt;</span> <span class="p">&lt;</span><span class="n">P</span><span class="p">:</span> <span class="n">P2</span><span class="p">&gt;(</span><span class="n">x</span><span class="p">:</span> <span class="n">P</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">P</span><span class="p">)</span> <span class="p">{}</span>
</code></pre></div>

<p>I was excluding those when I shouldn’t have. My check needed to let generic types through—I wanted to prevent false positives, but I couldn’t have any false negatives.</p>

<p>I also found one test that I couldn’t run directly as a file: it <code>import</code>ed something and that <code>import</code> was essential to the test failure. Fortunately, I realized that the test suite prints out the whole <code>swiftc</code> invocation with the test failure. I was able to copy this bit, paste it in my Xcode scheme, and debug the failure in Xcode:</p>

<pre><code>-frontend -target x86_64-apple-macosx10.9 -module-cache-path /Users/mdiep/Repositories/apple/build/Ninja-DebugAssert/swift-macosx-x86_64/swift-test-results/x86_64-apple-macosx10.9/clang-module-cache -sdk /Applications/Xcode10.0.0b6.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -swift-version 4 -emit-silgen -verify-syntax-tree -module-name protocol_resilience -I /Users/mdiep/Repositories/apple/build/Ninja-DebugAssert/swift-macosx-x86_64/test-macosx-x86_64/SILGen/Output/protocol_resilience.swift.tmp -enable-sil-ownership -enable-resilience /Users/mdiep/Repositories/apple/swift/test/SILGen/protocol_resilience.swift
</code></pre>

<p>At this point, I opened a PR. Despite my underwhelming PR description, I eventually communicated what I was trying to achieve. <a href="https://github.com/slavapestov">Slava Pestov</a> gave me some helpful feedback:</p>

<ol>
<li>My check for whether the type was generic could be simplified</li>
<li>I hadn’t casted the <code>FuncDecl</code> correctly</li>
<li>He suggested that I try to unify the function I’d copied with its source</li>
<li>He requested some more tests</li>
<li>I’d passed around an object describing the conformance to the protocol, but I didn’t need to</li>
</ol>

<p>This was all good feedback. And after (1), (3) seemed more doable. Ultimately I ended up with this:</p>

<div class="codehilite"><pre><span></span><code><span class="n">bool</span> <span class="n">swift</span><span class="p">::</span><span class="n">isMemberOperator</span><span class="p">(</span><span class="n">FuncDecl</span> <span class="o">*</span><span class="n">decl</span><span class="p">,</span> <span class="kr">Type</span> <span class="n">type</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Check that member operators reference the type of &#39;Self&#39;.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">decl</span><span class="p">-&gt;</span><span class="n">isInvalid</span><span class="p">())</span>
    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>

  <span class="n">auto</span> <span class="o">*</span><span class="n">DC</span> <span class="p">=</span> <span class="n">decl</span><span class="p">-&gt;</span><span class="n">getDeclContext</span><span class="p">();</span>
  <span class="n">auto</span> <span class="n">selfNominal</span> <span class="p">=</span> <span class="n">DC</span><span class="p">-&gt;</span><span class="n">getSelfNominalTypeDecl</span><span class="p">();</span>

  <span class="c1">// Check the parameters for a reference to &#39;Self&#39;.</span>
  <span class="n">bool</span> <span class="n">isProtocol</span> <span class="p">=</span> <span class="n">selfNominal</span> <span class="o">&amp;&amp;</span> <span class="n">isa</span><span class="p">&lt;</span><span class="n">ProtocolDecl</span><span class="p">&gt;(</span><span class="n">selfNominal</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="n">auto</span> <span class="n">param</span> <span class="p">:</span> <span class="o">*</span><span class="n">decl</span><span class="p">-&gt;</span><span class="n">getParameters</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">auto</span> <span class="n">paramType</span> <span class="p">=</span> <span class="n">param</span><span class="p">-&gt;</span><span class="n">getInterfaceType</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">paramType</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>

    <span class="c1">// Look through a metatype reference, if there is one.</span>
    <span class="n">paramType</span> <span class="p">=</span> <span class="n">paramType</span><span class="p">-&gt;</span><span class="n">getMetatypeInstanceType</span><span class="p">();</span>

    <span class="n">auto</span> <span class="n">nominal</span> <span class="p">=</span> <span class="n">paramType</span><span class="p">-&gt;</span><span class="n">getAnyNominal</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">type</span><span class="p">.</span><span class="n">isNull</span><span class="p">())</span> <span class="p">{</span>
      <span class="c1">// Is it the same nominal type?</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">selfNominal</span> <span class="o">&amp;&amp;</span> <span class="n">nominal</span> <span class="p">==</span> <span class="n">selfNominal</span><span class="p">)</span>
        <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="c1">// Is it the same nominal type? Or a generic (which may or may not match)?</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">paramType</span><span class="p">-&gt;</span><span class="k">is</span><span class="p">&lt;</span><span class="n">GenericTypeParamType</span><span class="p">&gt;()</span> <span class="o">||</span>
          <span class="n">nominal</span> <span class="p">==</span> <span class="n">type</span><span class="p">-&gt;</span><span class="n">getAnyNominal</span><span class="p">())</span>
        <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">isProtocol</span><span class="p">)</span> <span class="p">{</span>
      <span class="c1">// For a protocol, is it the &#39;Self&#39; type parameter?</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">auto</span> <span class="n">genericParam</span> <span class="p">=</span> <span class="n">paramType</span><span class="p">-&gt;</span><span class="n">getAs</span><span class="p">&lt;</span><span class="n">GenericTypeParamType</span><span class="p">&gt;())</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">genericParam</span><span class="p">-&gt;</span><span class="n">isEqual</span><span class="p">(</span><span class="n">DC</span><span class="p">-&gt;</span><span class="n">getSelfInterfaceType</span><span class="p">()))</span>
          <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>

<p>This worked with the new case I was adding (checking in the context of a specific type’s conformance) but also worked when checking the protocol itself.</p>

<p>Now instead of noting all 64 <code>==</code>s in the standard library, it only noted 8 that were generic!</p>

<p>The PR is <a href="https://github.com/apple/swift/pull/18831">here</a>.</p>

    ]]></content>
  </entry>

  <entry>
    <title>Entry 6: Unexpected Arguments Diagnostic</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2018-07-24T12:30:26Z</published>
    <updated>2018-07-24T12:30:26Z</updated>
    <content type='html'><![CDATA[
    <p><a href="http://matt.diephouse.com/swift/5/">As promised</a>, I returned to <a href="https://bugs.swift.org/browse/SR-4270">SR-4270</a>. This code used to return a really terrible diagnostic:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">let</span> <span class="nv">x</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">=</span> <span class="kc">true</span>
<span class="kd">enum</span> <span class="nc">A</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">a</span>
    <span class="k">case</span> <span class="n">b</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">f</span><span class="p">(</span><span class="kc">_</span> <span class="n">a</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span> <span class="p">{}</span>
<span class="kd">let</span> <span class="nv">a</span><span class="p">:</span> <span class="n">A</span> <span class="p">=</span> <span class="p">.</span><span class="n">a</span>
<span class="n">f</span><span class="p">(</span><span class="n">x</span> <span class="p">?</span> <span class="p">.</span><span class="n">a</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="p">:</span> <span class="p">.</span><span class="n">b</span><span class="p">)</span>
</code></pre></div>

<p>In Swift 4.2, it returned a much improved diagnostic:</p>

<pre><code>error: member 'a' takes no arguments
f(x ? .a(a) : .b)
       ^~~~
</code></pre>

<p>But <a href="https://twitter.com/UINT_MIN">Jordan Rose</a> <a href="https://bugs.swift.org/browse/SR-4270?focusedCommentId=37491&amp;page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-37491">suggested</a> that it would be better if it explicitly mentioned enums.</p>

<p>I followed my usual approach for improving diagnostics:</p>

<ol>
<li>Search <code>DiagnosticsSema.def</code> for the ID of the diagnostic I want to improve</li>
<li>Search the code for that ID</li>
<li>Actually improve the code there</li>
</ol>

<p>I quickly found the relevant code in <code>CSDiag.cpp</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="k">case</span> <span class="nl">CC_GeneralMismatch</span><span class="p">:</span> <span class="p">{</span> <span class="c1">// Something else is wrong.</span>
  <span class="c1">// If an argument value was specified, but this member expects no</span>
  <span class="c1">// arguments,</span>
  <span class="c1">// then we fail with a nice error message.</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">candidateArgTy</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">CS</span><span class="p">.</span><span class="n">getType</span><span class="p">(</span><span class="n">argExpr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">isVoid</span><span class="p">())</span> <span class="p">{</span>
      <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
               <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_parens_in_contextual_member</span><span class="p">,</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
          <span class="p">.</span><span class="n">fixItRemove</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">());</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
               <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_argument_in_contextual_member</span><span class="p">,</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
          <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">());</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>

<p>This code used 2 diagnostics:</p>

<pre><code>ERROR(unexpected_argument_in_contextual_member,none,
      "member %0 takes no arguments", (DeclName))
ERROR(unexpected_parens_in_contextual_member,none,
      "member %0 is not a function", (DeclName))
</code></pre>

<p>Pretty straightforward. If arguments were given, but none were expected, then the compiler either (1) suggests that you remove the parens if they’re empty or (2) points out that it doesn’t take any arguments.</p>

<p>The hardest part of improving diagnostics is always deciding what the improvement should actually be.</p>

<h2>Attempt 1</h2>

<p>My first thought was to add a check for an enum and add separate diagnostics in that case. I wasn’t sure how to do that, but I started looking through the rest of the method I was in.</p>

<p>I found that several other diagnostics used <code>CS.getContextualType()</code>—including a test that checked what kind of type it was, <code>CS.getContextualType()-&gt;is&lt;UnboundGenericType&gt;()</code>. (<em>CS</em> is commonly used as an abbreviation for <em>constraint system</em> within the compiler.)</p>

<p>The contextual type looked like the right thing and it made sense—this was a <em>contextual member lookup</em>. In other words, the compiler had <code>.foo</code>, a member <code>foo</code>, and had to look up what it referred to by using the context—the type wasn’t explicitly given.</p>

<p>So I added 2 new diagnostics:</p>

<pre><code>ERROR(unexpected_argument_in_enum_case,none,
      "enum case %0 takes no arguments", (DeclName))
ERROR(unexpected_parens_in_enum_case,none,
      "enum case %0 is not a function", (DeclName))
</code></pre>

<p>And added code that used them:</p>

<div class="codehilite"><pre><span></span><code><span class="k">if</span> <span class="p">(</span><span class="n">CS</span><span class="p">.</span><span class="n">getContextualType</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">is</span><span class="o">&lt;</span><span class="n">EnumType</span><span class="o">&gt;</span><span class="p">())</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">CS</span><span class="p">.</span><span class="n">getType</span><span class="p">(</span><span class="n">argExpr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">isVoid</span><span class="p">())</span> <span class="p">{</span>
      <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
               <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_parens_in_enum_case</span><span class="p">,</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
          <span class="p">.</span><span class="n">fixItRemove</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">());</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
               <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_argument_in_enum_case</span><span class="p">,</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
          <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">());</span>
    <span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">CS</span><span class="p">.</span><span class="n">getType</span><span class="p">(</span><span class="n">argExpr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">isVoid</span><span class="p">())</span> <span class="p">{</span>
      <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
               <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_parens_in_contextual_member</span><span class="p">,</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
          <span class="p">.</span><span class="n">fixItRemove</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">());</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
               <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_argument_in_contextual_member</span><span class="p">,</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
          <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">());</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>That worked!</p>

<pre><code>error: enum case 'a' takes no arguments
f(x ? .a(a) : .b)
       ^~~~
</code></pre>

<p>But the code seemed awfully repetitive, so I wasn’t happy with it.</p>

<h2>Attempt 2</h2>

<p>In <a href="http://matt.diephouse.com/swift/5/">Entry 5</a>, I discovered that <code>DescriptiveDeclKind</code>s can be passed to a diagnostic to print a <em>description</em> of the <em>kind</em> of <em>declaration</em>. (I had changed the description of “enum elements” to say “enum case”.) I thought that I might be able to use that here—if I could figure out how to get a <code>DescriptiveDeclKind</code>.</p>

<p>First I updated the original diagnostics:</p>

<pre><code>ERROR(unexpected_argument_in_contextual_member,none,
      "%0 %1 takes no arguments", (DescriptiveDeclKind, DeclName))
ERROR(unexpected_parens_in_contextual_member,none,
      "%0 %1 is not a function", (DescriptiveDeclKind, DeclName))
</code></pre>

<p>I knew that I’d have to get this from the <code>Decl</code> because of the method I’d updated previously:</p>

<div class="codehilite"><pre><span></span><code><span class="n">DescriptiveDeclKind</span> <span class="n">Decl</span><span class="o">::</span><span class="n">getDescriptiveKind</span><span class="p">()</span> <span class="k">const</span>
</code></pre></div>

<p>A <code>Decl</code> is a <em>declaration</em>. It refers to the declaration of anything in Swift—a function, a type, an enum case, etc. In this case, the compiler was trying to pick the right declaration. Looking through the method, I found that there would only be one candidate at this point:</p>

<div class="codehilite"><pre><span></span><code><span class="c1">// Dump all of our viable candidates into a CalleeCandidateInfo &amp; sort it</span>
<span class="c1">// out.</span>
<span class="n">CalleeCandidateInfo</span> <span class="nf">candidateInfo</span><span class="p">(</span><span class="n">Type</span><span class="p">(),</span> <span class="n">candidates</span><span class="p">,</span> <span class="n">hasTrailingClosure</span><span class="p">,</span>
                                  <span class="n">CS</span><span class="p">);</span>

<span class="c1">// Filter the candidate list based on the argument we may or may not have.</span>
<span class="n">candidateInfo</span><span class="p">.</span><span class="n">filterContextualMemberList</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">());</span>

<span class="c1">// If we have multiple candidates, then we have an ambiguity.</span>
<span class="k">if</span> <span class="p">(</span><span class="n">candidateInfo</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">SourceRange</span> <span class="n">argRange</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">arg</span> <span class="o">=</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">())</span>
    <span class="n">argRange</span> <span class="o">=</span> <span class="n">arg</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">();</span>
  <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">ambiguous_member_overload_set</span><span class="p">,</span>
           <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
      <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">argRange</span><span class="p">);</span>
  <span class="n">candidateInfo</span><span class="p">.</span><span class="n">suggestPotentialOverloads</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">().</span><span class="n">getBaseNameLoc</span><span class="p">());</span>
  <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>

<p>So I looked through the types to find that <code>CalleeCandidateInfo</code> had a list of <code>UncurriedCandidate</code>s (I knew there’d only be 1 at this point) which had a <code>getDecl()</code> method. Bingo!</p>

<p>So now I could get the <code>DescriptiveDeclKind</code> and pass it to my diagnostic:</p>

<div class="codehilite"><pre><span></span><code><span class="k">auto</span> <span class="n">kind</span> <span class="o">=</span> <span class="n">candidateInfo</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">getDecl</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getDescriptiveKind</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">CS</span><span class="p">.</span><span class="n">getType</span><span class="p">(</span><span class="n">argExpr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">isVoid</span><span class="p">())</span> <span class="p">{</span>
  <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_parens_in_contextual_member</span><span class="p">,</span>
           <span class="n">kind</span><span class="p">,</span>
           <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
       <span class="p">.</span><span class="n">fixItRemove</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">());</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_argument_in_contextual_member</span><span class="p">,</span>
           <span class="n">kind</span><span class="p">,</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
       <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div>

<p>That also worked, but I realized that the diagnostic wasn’t great as it could be—especially if the parens were empty.</p>

<pre><code>error: enum case 'a' takes no arguments
f(x ? .a(a) : .b)
       ^~~~
</code></pre>

<p>But I ran the test suite. I wanted to see (1) where these enum diagnostics were tested and (2) what other types of declarations would have this error. I found some enum tests in <code>test/decl/enum/enumtest.swift</code>, but to my surprise (horror?) I found that there weren’t any other test cases.</p>

<p>I thought about it and figured I could get a similar error by adding a <code>static var</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">struct</span> <span class="nc">A</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="kd">var</span> <span class="nv">a</span><span class="p">:</span> <span class="n">A</span> <span class="p">{</span> <span class="k">return</span> <span class="n">A</span><span class="p">()</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">let</span> <span class="nv">_</span><span class="p">:</span> <span class="n">A</span> <span class="p">=</span> <span class="p">.</span><span class="n">a</span><span class="p">()</span>
<span class="kd">let</span> <span class="nv">_</span><span class="p">:</span> <span class="n">A</span> <span class="p">=</span> <span class="p">.</span><span class="n">a</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div>

<p>This yielded these errors:</p>

<pre><code>error: static var 'a' takes no arguments
let _: A = .a()
             ^~~~

error: static var 'a' is not a function
let _: A = .a(1)
             ^~~~
</code></pre>

<p>The second error made sense to me, but the first didn’t. Of course a <code>static var</code> takes no arguments. It’s not a function.</p>

<h2>Attempt 3</h2>

<p>This led me to my final attempt.</p>

<p>I decided that there should be just 2 diagnostics:</p>

<pre><code>ERROR(unexpected_arguments_in_enum_case,none,
      "enum case %0 has no associated values", (DeclName))
ERROR(unexpected_arguments_in_contextual_member,none,
      "%0 %1 is not a function", (DescriptiveDeclKind, DeclName))
</code></pre>

<p>The distinction between having arguments or no arguments wasn’t important for the actual message—it only mattered for whether the compiler should suggest a fixit to just remove the empty <code>()</code>. If the type was an enum, I wanted a message about associated values; otherwise I wanted a message about not being a function.</p>

<p>So I updated the code I’d written to use these:</p>

<div class="codehilite"><pre><span></span><code><span class="kt">bool</span> <span class="n">isEnum</span> <span class="o">=</span> <span class="n">CS</span><span class="p">.</span><span class="n">getContextualType</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">is</span><span class="o">&lt;</span><span class="n">EnumType</span><span class="o">&gt;</span><span class="p">();</span>
<span class="kt">bool</span> <span class="n">isVoid</span> <span class="o">=</span> <span class="n">CS</span><span class="p">.</span><span class="n">getType</span><span class="p">(</span><span class="n">argExpr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">isVoid</span><span class="p">();</span>
<span class="k">auto</span> <span class="n">argumentRange</span> <span class="o">=</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">isEnum</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">isVoid</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_arguments_in_enum_case</span><span class="p">,</span>
             <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
        <span class="p">.</span><span class="n">fixItRemove</span><span class="p">(</span><span class="n">argumentRange</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_arguments_in_enum_case</span><span class="p">,</span>
             <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
        <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">argumentRange</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="k">auto</span> <span class="n">kind</span> <span class="o">=</span> <span class="n">candidateInfo</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">getDecl</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getDescriptiveKind</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">isVoid</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
             <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_arguments_in_contextual_member</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span>
             <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
        <span class="p">.</span><span class="n">fixItRemove</span><span class="p">(</span><span class="n">argumentRange</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
             <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_arguments_in_contextual_member</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span>
             <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
        <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">argumentRange</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>This seemed good. It provided the diagnostics I wanted. I ran the test suite and all tests passed. So I set to work adding some tests for the <code>static var</code> case.</p>

<p>I looked through the other diagnostics in this case to find where they were tested. Then I’d have a good spot to add my additional tests. I found some code in <code>test/Constraints/diagnostics.swift</code> and updated it with my test:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">enum</span> <span class="nc">Color</span> <span class="p">{</span>
  <span class="k">case</span> <span class="n">Red</span>
  <span class="err">…</span>
  <span class="kd">static</span> <span class="kd">var</span> <span class="nv">svar</span><span class="p">:</span> <span class="n">Color</span> <span class="p">{</span> <span class="k">return</span> <span class="p">.</span><span class="n">Red</span> <span class="p">}</span>
<span class="p">}</span>

<span class="err">…</span>
<span class="kd">var</span> <span class="nv">someColor</span> <span class="p">:</span> <span class="n">Color</span>
<span class="err">…</span>
<span class="n">someColor</span> <span class="p">=</span> <span class="p">.</span><span class="n">svar</span><span class="p">()</span> <span class="c1">// expected-error {{static var &#39;svar&#39; is not a function}}</span>
<span class="n">someColor</span> <span class="p">=</span> <span class="p">.</span><span class="n">svar</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">// expected-error {{static var &#39;svar&#39; is not a function}}</span>
</code></pre></div>

<p>I reran these tests, expecting to see them pass. But they didn’t.</p>

<pre><code>test/Constraints/diagnostics.swift:456:41: error: incorrect message found
someColor = .svar() // expected-error {{static var 'svar' is not a function}}
                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                        enum case 'svar' has no associated values
test/Constraints/diagnostics.swift:457:42: error: incorrect message found
someColor = .svar(1) // expected-error {{static var 'svar' is not a function}}
                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                         enum case 'svar' has no associated values
</code></pre>

<p>This was a lucky find. I had been testing with a <code>static var</code> on a <code>struct</code>. The test I added used a <code>static var</code> on an <code>enum</code>. That meant that the contextual type was an enum, so I was using the enum diagnostic. But that was wrong: the contextual type here was an enum, but the member was a <code>static var</code>.</p>

<p>Instead, I should have been testing whether the declaration was an enum element. Luckily, I already had the <code>DescriptiveDeclKind</code>. So this was an easy fix.</p>

<div class="codehilite"><pre><span></span><code><span class="k">auto</span> <span class="n">kind</span> <span class="o">=</span> <span class="n">candidateInfo</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">getDecl</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getDescriptiveKind</span><span class="p">();</span>
<span class="kt">bool</span> <span class="n">isVoid</span> <span class="o">=</span> <span class="n">CS</span><span class="p">.</span><span class="n">getType</span><span class="p">(</span><span class="n">argExpr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">isVoid</span><span class="p">();</span>
<span class="k">auto</span> <span class="n">argumentRange</span> <span class="o">=</span> <span class="n">E</span><span class="o">-&gt;</span><span class="n">getArgument</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getSourceRange</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">kind</span> <span class="o">==</span> <span class="n">DescriptiveDeclKind</span><span class="o">::</span><span class="n">EnumElement</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">isVoid</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_arguments_in_enum_case</span><span class="p">,</span>
             <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
        <span class="p">.</span><span class="n">fixItRemove</span><span class="p">(</span><span class="n">argumentRange</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_arguments_in_enum_case</span><span class="p">,</span>
             <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
        <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">argumentRange</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">isVoid</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
             <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_arguments_in_contextual_member</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span>
             <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
        <span class="p">.</span><span class="n">fixItRemove</span><span class="p">(</span><span class="n">argumentRange</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">diagnose</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">getNameLoc</span><span class="p">(),</span>
             <span class="n">diag</span><span class="o">::</span><span class="n">unexpected_arguments_in_contextual_member</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span>
             <span class="n">E</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">())</span>
        <span class="p">.</span><span class="n">highlight</span><span class="p">(</span><span class="n">argumentRange</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>I tested again to verify that it worked as expected. Then I was able open <a href="https://github.com/apple/swift/pull/18167">my PR</a>. Diagnostic improved!</p>

<p>I still have a lot to learn about the Swift compiler, but I’m starting to feel comfortable making improvements to diagnostics. That’s pretty exciting to me: I feel passionate about improving these.</p>

<p>If you’re interested in contributing to Swift, please try improving a diagnostic! Hopefully what I’ve written on this blog can help. But if you’re stuck, feel free to reach out to me or <a href="https://twitter.com/mdiep/status/1005542789134897152">join the (very unofficial) Swift Contributors Slack</a>.</p>

    ]]></content>
  </entry>

  <entry>
    <title>Entry 5: Enum Element Diagnostics</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2018-07-16T13:02:52Z</published>
    <updated>2018-07-16T13:02:52Z</updated>
    <content type='html'><![CDATA[
    <p>As I look for Swift bugs to work on, I’ve been testing bugs to see if they’re still reproducible. Many of them aren’t—particularly old type-checker issues—so they can be closed.</p>

<p>Take <a href="https://bugs.swift.org/browse/SR-4270">SR-4270</a> as an example.</p>

<div class="codehilite"><pre><span></span><code><span class="kd">let</span> <span class="nv">x</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">=</span> <span class="kc">true</span>
<span class="kd">enum</span> <span class="nc">A</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">a</span>
    <span class="k">case</span> <span class="n">b</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">f</span><span class="p">(</span><span class="kc">_</span> <span class="n">a</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span> <span class="p">{}</span>
<span class="kd">let</span> <span class="nv">a</span><span class="p">:</span> <span class="n">A</span> <span class="p">=</span> <span class="p">.</span><span class="n">a</span>
<span class="n">f</span><span class="p">(</span><span class="n">x</span> <span class="p">?</span> <span class="p">.</span><span class="n">a</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="p">:</span> <span class="p">.</span><span class="n">b</span><span class="p">)</span> <span class="c1">// .a(a) is wrong. A.a takes no arguments.</span>
</code></pre></div>

<p>This used to produce a terrible diagnostic:</p>

<pre><code>error: result values in '? :' expression have mismatching types '' and ''
f(x ? .a(a) : .b)
~~~~~ ^ ~~
</code></pre>

<p>But in Swift 4.2, the diagnostic is much better:</p>

<pre><code>error: member 'a' takes no arguments
f(x ? .a(a) : .b)
       ^~~~
</code></pre>

<p>I closed the bug as <em>cannot reproduce</em>, but <a href="https://twitter.com/UINT_MIN">Jordan Rose</a> suggested that it could be even better if it explicitly mentioned that it was part of an <code>enum</code>. Something like <code>enum element 'a' does not have an associated value</code>.</p>

<p>I’m going to do that. But before I got started on it, I decided to look at <code>enum element</code>. When I’m looking at Swift errors, I usually infer quite a bit based on context. It can be hard to critically examine the error messages while I’m using them. But <code>enum element</code> seemed really strange now that I was thinking critically.</p>

<p>Looking through the other diagnostics, there <em>were</em> a few that mentioned <code>enum element</code>. But it seemed much clearer, at least to me, to say <code>enum case</code> instead.</p>

<p>By looking at the code that emitted these diagnostics, I found that the compiler has both <code>EnumCaseDecl</code>s and <code>EnumElementDecl</code>s in its AST. That’s because a <code>case</code> can actually name multiple elements:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">enum</span> <span class="nc">Planet</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">mercury</span><span class="p">,</span> <span class="n">venus</span><span class="p">,</span> <span class="n">earth</span><span class="p">,</span> <span class="n">mars</span><span class="p">,</span> <span class="n">jupiter</span><span class="p">,</span> <span class="n">saturn</span><span class="p">,</span> <span class="n">uranus</span><span class="p">,</span> <span class="n">neptune</span>
<span class="p">}</span>
</code></pre></div>

<p>I don’t think I’d ever write an <code>enum</code> like that, so I’d forgotten about it. But it makes sense that the compiler would need different names for these internally. But using internal names from the compiler usually doesn’t make for clear diagnostics.</p>

<p>I searched for <code>swift enum element</code>, to see if anyone else used this term, and the only results were people asking what these error messages meant. That’s a good sign that they could be improved. But I also searched <a href="https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html">the Swift documentation about enums</a>. The only use of <em>element</em> there is describing the contents of an array. Additionally, the <code>Planet</code> sample I copied above had this description:</p>

<blockquote>
  <p>Multiple cases can appear on a single line, separated by commas</p>
</blockquote>

<p>That seemed like pretty good evidence that <code>enum case</code> would be a better diagnostic:</p>

<ol>
<li>There’s no evidence that <code>enum element</code> is a widely recognized/used term</li>
<li>The documentation never uses that term</li>
<li>The documentation explicitly calls these <code>case</code>s</li>
</ol>

<p>So I decided to go ahead and open a PR to do that.</p>

<p>I used <code>ag</code> to search for occurrences of <code>enum element</code>. I found one diagnostic that used it directly, so I fixed that one.</p>

<p>But I also looked through the tests directory and found some other diagnostics that did. After fixing up those test expectations, I went to look for where they were emitted. They hadn’t turned up in my search. Here’s one example:</p>

<div class="codehilite"><pre><span></span><code><span class="kr">@objc</span>
<span class="kd">enum</span> <span class="nc">BadEnum2</span><span class="p">:</span> <span class="nb">Int</span> <span class="p">{</span>
  <span class="kr">@objc</span><span class="p">(</span><span class="n">X</span><span class="p">:)</span>   <span class="c1">// expected-error{{&#39;@objc&#39; enum element must have a simple name}}{{10-11=}}</span>
  <span class="k">case</span> <span class="n">X</span>
<span class="p">}</span>
</code></pre></div>

<p>I searched for that diagnostic and found that it didn’t use <code>enum element</code> directly:</p>

<pre><code>ERROR(objc_name_req_nullary,none,
      "'@objc' %0 must have a simple name", (DescriptiveDeclKind))
</code></pre>

<p><code>DescriptiveDeclKind</code> here is the type of parameter that’s being passed to the format string. From previous fixes I’ve made, I knew that the <code>EnumElementDecl</code> must be getting passed to this—and something was rendering that as <code>enum element</code>.</p>

<p>So I did the simple thing: I searched the code for <code>”enum element”</code>. Luckily, that turned up something that looked like what I wanted:</p>

<div class="codehilite"><pre><span></span><code><span class="n">StringRef</span> <span class="n">Decl</span><span class="o">::</span><span class="n">getDescriptiveKindName</span><span class="p">(</span><span class="n">DescriptiveDeclKind</span> <span class="n">K</span><span class="p">)</span> <span class="p">{</span>
<span class="cp">#define ENTRY(Kind, String) case DescriptiveDeclKind::Kind: return String</span>
  <span class="k">switch</span> <span class="p">(</span><span class="n">K</span><span class="p">)</span> <span class="p">{</span>
  <span class="err">…</span> <span class="c1">// other cases omitted for brevity</span>
  <span class="n">ENTRY</span><span class="p">(</span><span class="n">EnumElement</span><span class="p">,</span> <span class="s">&quot;enum element&quot;</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>I had a feeling that changing this to <code>”enum case”</code> would do what I wanted. I changed it, ran <code>utils/build-script --debug --test</code>, and waited.</p>

<p>That worked! I had to fix up a couple of tests, but I was ready to open a PR. You can view it <a href="https://github.com/apple/swift/pull/17964">here</a>.</p>

<p>I think improving diagnostics is great way to contribute to the Swift compiler. It’s comparatively quite easy—in some cases it’s literally just editing strings! I also find it compelling because you can (a) eliminate confusion and (b) increase programmer productivity. The latter <a href="http://matt.diephouse.com/2018/05/better-faster-cheaper/">hopefully means that we can write better software</a>.</p>

<p>If you have to google or ask about an error message, then it can be improved. So anytime you do, consider opening a PR to fix it!</p>

    ]]></content>
  </entry>

  <entry>
    <title>Entry 4: Derive Equatable & Hashable for Uninhabited Types</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2018-07-14T17:32:05Z</published>
    <updated>2018-07-14T17:32:05Z</updated>
    <content type='html'><![CDATA[
    <p>After attempting a few failed improvements, I needed another win. I looked through a few tickets, but then I remembered a limitation of Swift that has bothered me: Swift won’t synthesize <code>Equatable</code> and <code>Hashable</code> implementations for uninhabited types.</p>

<p>An uninhabited type is one that can’t be initialized. <code>Never</code>, which is included in the standard library, is one such type:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">public</span> <span class="kd">enum</span> <span class="nc">Never</span> <span class="p">{}</span>
</code></pre></div>

<p>A type like this can easily be made <code>Equatable</code> and <code>Hashable</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">extension</span> <span class="nc">Never</span><span class="p">:</span> <span class="nb">Equatable</span> <span class="p">{</span>
  <span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="p">==</span> <span class="p">(</span><span class="n">lhs</span><span class="p">:</span> <span class="n">Never</span><span class="p">,</span> <span class="n">rhs</span><span class="p">:</span> <span class="n">Never</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Bool</span> <span class="p">{</span>
    <span class="c1">// This is actually valid Swift. No `case`s are needed because</span>
    <span class="c1">// every _possible_ value has been included.</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">lhs</span><span class="p">,</span> <span class="n">rhs</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="nc">Never</span><span class="p">:</span> <span class="nb">Hashable</span> <span class="p">{</span>
  <span class="kd">public</span> <span class="kd">var</span> <span class="nv">hashValue</span><span class="p">:</span> <span class="nb">Int</span> <span class="p">{</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">lhs</span><span class="p">,</span> <span class="n">rhs</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>But Swift won’t do this for you, even though it’ll do it if you include one or more cases:</p>

<div class="codehilite"><pre><span></span><code><span class="c1">// Swift complains that you haven&#39;t implemented the required methods</span>
<span class="kd">enum</span> <span class="nc">NoCases</span><span class="p">:</span> <span class="nb">Hashable</span> <span class="p">{</span>
<span class="p">}</span>

<span class="c1">// But this is okay</span>
<span class="kd">enum</span> <span class="nc">OneCase</span><span class="p">:</span> <span class="nb">Hashable</span> <span class="p">{</span>
  <span class="k">case</span> <span class="n">one</span>
<span class="p">}</span>
</code></pre></div>

<p>So I set out to fix this.</p>

<p>The first task was to find the code for synthesized implementations. I don’t remember how (it was a couple weeks ago due to some personal reasons), but I found this code in <code>DerivedConformanceEquatableHashable.cpp</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="c1">/// Common preconditions for Equatable and Hashable.</span>
<span class="k">static</span> <span class="kt">bool</span> <span class="nf">canDeriveConformance</span><span class="p">(</span><span class="n">TypeChecker</span> <span class="o">&amp;</span><span class="n">tc</span><span class="p">,</span> <span class="n">DeclContext</span> <span class="o">*</span><span class="n">DC</span><span class="p">,</span>
                                 <span class="n">NominalTypeDecl</span> <span class="o">*</span><span class="n">target</span><span class="p">,</span>
                                 <span class="n">ProtocolDecl</span> <span class="o">*</span><span class="n">protocol</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// The type must be an enum or a struct.</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">enumDecl</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">EnumDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">target</span><span class="p">))</span> <span class="p">{</span>
    <span class="c1">// The enum must have cases.</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">enumDecl</span><span class="o">-&gt;</span><span class="n">hasCases</span><span class="p">())</span>
      <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

    <span class="c1">// The cases must not have associated values, or all associated values must</span>
    <span class="c1">// conform to the protocol.</span>
    <span class="k">return</span> <span class="n">allAssociatedValuesConformToProtocol</span><span class="p">(</span><span class="n">tc</span><span class="p">,</span> <span class="n">DC</span><span class="p">,</span> <span class="n">enumDecl</span><span class="p">,</span> <span class="n">protocol</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">structDecl</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">StructDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">target</span><span class="p">))</span> <span class="p">{</span>
    <span class="c1">// All stored properties of the struct must conform to the protocol.</span>
    <span class="k">return</span> <span class="n">allStoredPropertiesConformToProtocol</span><span class="p">(</span><span class="n">tc</span><span class="p">,</span> <span class="n">DC</span><span class="p">,</span> <span class="n">structDecl</span><span class="p">,</span> <span class="n">protocol</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>

<p>This method was returning <code>false</code> for any enums that didn’t have any <code>case</code>s. It couldn’t be as easy as removing that check, could it? 🤔</p>

<p>No, it couldn’t. I got this strange warning after removing that check:</p>

<pre><code>&lt;unknown&gt;:0: warning: will never be executed
</code></pre>

<p>But that was definitely the right first step.</p>

<p>Next, I hunted down the source of that warning. It was in the SIL (Swift Intermediate Language) generation, so it didn’t turn up anything useful.</p>

<p>So, instead, I backed up, set a breakpoint in the <code>canDeriveConformance</code> method that I mentioned above, and walked through the code. Just by stepping over, I ended up in <code>resolveWitnessViaDerivation</code>, which had this line:</p>

<div class="codehilite"><pre><span></span><code><span class="c1">// Attempt to derive the witness.</span>
<span class="k">auto</span> <span class="n">derived</span> <span class="o">=</span> <span class="n">TC</span><span class="p">.</span><span class="n">deriveProtocolRequirement</span><span class="p">(</span><span class="n">DC</span><span class="p">,</span> <span class="n">derivingTypeDecl</span><span class="p">,</span> <span class="n">requirement</span><span class="p">);</span>
</code></pre></div>

<p>Stepping into that led me to the code where the <code>Equatable</code> implementation is derived:</p>

<div class="codehilite"><pre><span></span><code><span class="n">ValueDecl</span> <span class="o">*</span><span class="n">DerivedConformance</span><span class="o">::</span><span class="n">deriveEquatable</span><span class="p">(</span><span class="n">ValueDecl</span> <span class="o">*</span><span class="n">requirement</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">checkAndDiagnoseDisallowedContext</span><span class="p">(</span><span class="n">requirement</span><span class="p">))</span>
    <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>

  <span class="c1">// Build the necessary decl.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">requirement</span><span class="o">-&gt;</span><span class="n">getBaseName</span><span class="p">()</span> <span class="o">==</span> <span class="s">&quot;==&quot;</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">ED</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">EnumDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">Nominal</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">auto</span> <span class="n">bodySynthesizer</span> <span class="o">=</span>
          <span class="n">ED</span><span class="o">-&gt;</span><span class="n">hasOnlyCasesWithoutAssociatedValues</span><span class="p">()</span>
              <span class="o">?</span> <span class="o">&amp;</span><span class="nl">deriveBodyEquatable_enum_noAssociatedValues_eq</span>
              <span class="p">:</span> <span class="o">&amp;</span><span class="n">deriveBodyEquatable_enum_hasAssociatedValues_eq</span><span class="p">;</span>
      <span class="k">return</span> <span class="nf">deriveEquatable_eq</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">TC</span><span class="p">.</span><span class="n">Context</span><span class="p">.</span><span class="n">Id_derived_enum_equals</span><span class="p">,</span>
                                <span class="n">bodySynthesizer</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">isa</span><span class="o">&lt;</span><span class="n">StructDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">Nominal</span><span class="p">))</span>
      <span class="k">return</span> <span class="n">deriveEquatable_eq</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">TC</span><span class="p">.</span><span class="n">Context</span><span class="p">.</span><span class="n">Id_derived_struct_equals</span><span class="p">,</span>
                                <span class="o">&amp;</span><span class="n">deriveBodyEquatable_struct_eq</span><span class="p">);</span>
    <span class="k">else</span>
      <span class="nf">llvm_unreachable</span><span class="p">(</span><span class="s">&quot;todo&quot;</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">TC</span><span class="p">.</span><span class="n">diagnose</span><span class="p">(</span><span class="n">requirement</span><span class="o">-&gt;</span><span class="n">getLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">broken_equatable_requirement</span><span class="p">);</span>
  <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>
</code></pre></div>

<p>This was what I was looking for. If the requirement is to add <code>==</code>, then Swift looks to see if it’s an <code>enum</code> or a <code>struct</code> and synthesizes an appropriate implementation. Interestingly, the <code>enum</code> version has a check: the synthesized implementation is different depending on whether the <code>enum</code> has any associated values.</p>

<p>An <code>enum</code> without any <code>case</code>s has only <code>case</code>s without associated values, so that’s the implementation the compiler would be using.</p>

<div class="codehilite"><pre><span></span><code><span class="c1">/// Derive the body for an &#39;==&#39; operator for an enum that has no associated</span>
<span class="c1">/// values. This generates code that converts each value to its integer ordinal</span>
<span class="c1">/// and compares them, which produces an optimal single icmp instruction.</span>
<span class="k">static</span> <span class="kt">void</span>
<span class="nf">deriveBodyEquatable_enum_noAssociatedValues_eq</span><span class="p">(</span><span class="n">AbstractFunctionDecl</span> <span class="o">*</span><span class="n">eqDecl</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">auto</span> <span class="n">parentDC</span> <span class="o">=</span> <span class="n">eqDecl</span><span class="o">-&gt;</span><span class="n">getDeclContext</span><span class="p">();</span>
  <span class="n">ASTContext</span> <span class="o">&amp;</span><span class="n">C</span> <span class="o">=</span> <span class="n">parentDC</span><span class="o">-&gt;</span><span class="n">getASTContext</span><span class="p">();</span>

  <span class="k">auto</span> <span class="n">args</span> <span class="o">=</span> <span class="n">eqDecl</span><span class="o">-&gt;</span><span class="n">getParameterLists</span><span class="p">().</span><span class="n">back</span><span class="p">();</span>
  <span class="k">auto</span> <span class="n">aParam</span> <span class="o">=</span> <span class="n">args</span><span class="o">-&gt;</span><span class="n">get</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
  <span class="k">auto</span> <span class="n">bParam</span> <span class="o">=</span> <span class="n">args</span><span class="o">-&gt;</span><span class="n">get</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>

  <span class="k">auto</span> <span class="n">enumDecl</span> <span class="o">=</span> <span class="n">cast</span><span class="o">&lt;</span><span class="n">EnumDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">aParam</span><span class="o">-&gt;</span><span class="n">getType</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getAnyNominal</span><span class="p">());</span>

  <span class="c1">// Generate the conversion from the enums to integer indices.</span>
  <span class="n">SmallVector</span><span class="o">&lt;</span><span class="n">ASTNode</span><span class="p">,</span> <span class="mi">6</span><span class="o">&gt;</span> <span class="n">statements</span><span class="p">;</span>
  <span class="n">DeclRefExpr</span> <span class="o">*</span><span class="n">aIndex</span> <span class="o">=</span> <span class="n">convertEnumToIndex</span><span class="p">(</span><span class="n">statements</span><span class="p">,</span> <span class="n">parentDC</span><span class="p">,</span> <span class="n">enumDecl</span><span class="p">,</span>
                                           <span class="n">aParam</span><span class="p">,</span> <span class="n">eqDecl</span><span class="p">,</span> <span class="s">&quot;index_a&quot;</span><span class="p">);</span>
  <span class="n">DeclRefExpr</span> <span class="o">*</span><span class="n">bIndex</span> <span class="o">=</span> <span class="n">convertEnumToIndex</span><span class="p">(</span><span class="n">statements</span><span class="p">,</span> <span class="n">parentDC</span><span class="p">,</span> <span class="n">enumDecl</span><span class="p">,</span>
                                           <span class="n">bParam</span><span class="p">,</span> <span class="n">eqDecl</span><span class="p">,</span> <span class="s">&quot;index_b&quot;</span><span class="p">);</span>

  <span class="c1">// Generate the compare of the indices.</span>
  <span class="n">FuncDecl</span> <span class="o">*</span><span class="n">cmpFunc</span> <span class="o">=</span> <span class="n">C</span><span class="p">.</span><span class="n">getEqualIntDecl</span><span class="p">();</span>
  <span class="n">assert</span><span class="p">(</span><span class="n">cmpFunc</span> <span class="o">&amp;&amp;</span> <span class="s">&quot;should have a == for int as we already checked for it&quot;</span><span class="p">);</span>

  <span class="k">auto</span> <span class="n">fnType</span> <span class="o">=</span> <span class="n">cmpFunc</span><span class="o">-&gt;</span><span class="n">getInterfaceType</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">castTo</span><span class="o">&lt;</span><span class="n">FunctionType</span><span class="o">&gt;</span><span class="p">();</span>

  <span class="c1">// Leaving out some uninteresting code for brevity</span>
  <span class="n">Expr</span> <span class="o">*</span><span class="n">cmpFuncExpr</span> <span class="o">=</span> <span class="err">…</span>

  <span class="n">TupleExpr</span> <span class="o">*</span><span class="n">abTuple</span> <span class="o">=</span> <span class="n">TupleExpr</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="p">{</span> <span class="n">aIndex</span><span class="p">,</span> <span class="n">bIndex</span> <span class="p">},</span>
                                         <span class="p">{</span> <span class="p">},</span> <span class="p">{</span> <span class="p">},</span> <span class="n">SourceLoc</span><span class="p">(),</span>
                                         <span class="cm">/*HasTrailingClosure*/</span> <span class="nb">false</span><span class="p">,</span>
                                         <span class="cm">/*Implicit*/</span> <span class="nb">true</span><span class="p">);</span>

  <span class="k">auto</span> <span class="o">*</span><span class="n">cmpExpr</span> <span class="o">=</span> <span class="k">new</span> <span class="p">(</span><span class="n">C</span><span class="p">)</span> <span class="n">BinaryExpr</span><span class="p">(</span><span class="n">cmpFuncExpr</span><span class="p">,</span> <span class="n">abTuple</span><span class="p">,</span> <span class="cm">/*implicit*/</span> <span class="nb">true</span><span class="p">);</span>
  <span class="n">statements</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="k">new</span> <span class="p">(</span><span class="n">C</span><span class="p">)</span> <span class="n">ReturnStmt</span><span class="p">(</span><span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">cmpExpr</span><span class="p">));</span>

  <span class="n">BraceStmt</span> <span class="o">*</span><span class="n">body</span> <span class="o">=</span> <span class="n">BraceStmt</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">statements</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">());</span>
  <span class="n">eqDecl</span><span class="o">-&gt;</span><span class="n">setBody</span><span class="p">(</span><span class="n">body</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>

<p>The included documentation comment describes it well. Swift is generating an AST for the code it’s synthesizing. This leads to the error we were seeing:</p>

<ol>
<li>The compiler detects that some of the generated code will never be executed since the type is uninhabited</li>
<li>The source location is unknown because there isn’t one: it’s synthesized</li>
</ol>

<p>The other case, where an <code>enum</code> <em>does</em> have associated values, looks similar but actually generates a <code>switch</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="c1">// switch (a, b) { &lt;case statements&gt; }</span>
<span class="k">auto</span> <span class="n">aRef</span> <span class="o">=</span> <span class="k">new</span> <span class="p">(</span><span class="n">C</span><span class="p">)</span> <span class="n">DeclRefExpr</span><span class="p">(</span><span class="n">aParam</span><span class="p">,</span> <span class="n">DeclNameLoc</span><span class="p">(),</span> <span class="cm">/*implicit*/</span><span class="nb">true</span><span class="p">);</span>
<span class="k">auto</span> <span class="n">bRef</span> <span class="o">=</span> <span class="k">new</span> <span class="p">(</span><span class="n">C</span><span class="p">)</span> <span class="n">DeclRefExpr</span><span class="p">(</span><span class="n">bParam</span><span class="p">,</span> <span class="n">DeclNameLoc</span><span class="p">(),</span> <span class="cm">/*implicit*/</span><span class="nb">true</span><span class="p">);</span>
<span class="k">auto</span> <span class="n">abExpr</span> <span class="o">=</span> <span class="n">TupleExpr</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="p">{</span> <span class="n">aRef</span><span class="p">,</span> <span class="n">bRef</span> <span class="p">},</span> <span class="p">{},</span> <span class="p">{},</span>
                                <span class="n">SourceLoc</span><span class="p">(),</span> <span class="cm">/*HasTrailingClosure*/</span> <span class="nb">false</span><span class="p">,</span>
                                <span class="cm">/*implicit*/</span> <span class="nb">true</span><span class="p">);</span>
<span class="k">auto</span> <span class="n">switchStmt</span> <span class="o">=</span> <span class="n">SwitchStmt</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">LabeledStmtInfo</span><span class="p">(),</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">abExpr</span><span class="p">,</span>
                                     <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">cases</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">C</span><span class="p">);</span>
<span class="n">statements</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">switchStmt</span><span class="p">);</span>

<span class="k">auto</span> <span class="n">body</span> <span class="o">=</span> <span class="n">BraceStmt</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">statements</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">());</span>
<span class="n">eqDecl</span><span class="o">-&gt;</span><span class="n">setBody</span><span class="p">(</span><span class="n">body</span><span class="p">);</span>
</code></pre></div>

<p>Using these two versions, I was able to write a new version that works for uninhabited types. It takes advantage of the implementations I gave above: a <code>switch</code> can be empty of the <code>enum</code> has no <code>case</code>s. (I basically copied the associated values variant and neutered it.)</p>

<div class="codehilite"><pre><span></span><code><span class="k">static</span> <span class="kt">void</span>
<span class="nf">deriveBodyEquatable_enum_uninhabited_eq</span><span class="p">(</span><span class="n">AbstractFunctionDecl</span> <span class="o">*</span><span class="n">eqDecl</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">auto</span> <span class="n">parentDC</span> <span class="o">=</span> <span class="n">eqDecl</span><span class="o">-&gt;</span><span class="n">getDeclContext</span><span class="p">();</span>
  <span class="n">ASTContext</span> <span class="o">&amp;</span><span class="n">C</span> <span class="o">=</span> <span class="n">parentDC</span><span class="o">-&gt;</span><span class="n">getASTContext</span><span class="p">();</span>

  <span class="k">auto</span> <span class="n">args</span> <span class="o">=</span> <span class="n">eqDecl</span><span class="o">-&gt;</span><span class="n">getParameterLists</span><span class="p">().</span><span class="n">back</span><span class="p">();</span>
  <span class="k">auto</span> <span class="n">aParam</span> <span class="o">=</span> <span class="n">args</span><span class="o">-&gt;</span><span class="n">get</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
  <span class="k">auto</span> <span class="n">bParam</span> <span class="o">=</span> <span class="n">args</span><span class="o">-&gt;</span><span class="n">get</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>

  <span class="n">assert</span><span class="p">(</span><span class="o">!</span><span class="n">cast</span><span class="o">&lt;</span><span class="n">EnumDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">aParam</span><span class="o">-&gt;</span><span class="n">getType</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getAnyNominal</span><span class="p">())</span><span class="o">-&gt;</span><span class="n">hasCases</span><span class="p">());</span>

  <span class="n">SmallVector</span><span class="o">&lt;</span><span class="n">ASTNode</span><span class="p">,</span> <span class="mi">1</span><span class="o">&gt;</span> <span class="n">statements</span><span class="p">;</span>
  <span class="n">SmallVector</span><span class="o">&lt;</span><span class="n">ASTNode</span><span class="p">,</span> <span class="mi">0</span><span class="o">&gt;</span> <span class="n">cases</span><span class="p">;</span>

  <span class="c1">// switch (a, b) { }</span>
  <span class="k">auto</span> <span class="n">aRef</span> <span class="o">=</span> <span class="k">new</span> <span class="p">(</span><span class="n">C</span><span class="p">)</span> <span class="n">DeclRefExpr</span><span class="p">(</span><span class="n">aParam</span><span class="p">,</span> <span class="n">DeclNameLoc</span><span class="p">(),</span> <span class="cm">/*implicit*/</span> <span class="nb">true</span><span class="p">);</span>
  <span class="k">auto</span> <span class="n">bRef</span> <span class="o">=</span> <span class="k">new</span> <span class="p">(</span><span class="n">C</span><span class="p">)</span> <span class="n">DeclRefExpr</span><span class="p">(</span><span class="n">bParam</span><span class="p">,</span> <span class="n">DeclNameLoc</span><span class="p">(),</span> <span class="cm">/*implicit*/</span> <span class="nb">true</span><span class="p">);</span>
  <span class="k">auto</span> <span class="n">abExpr</span> <span class="o">=</span> <span class="n">TupleExpr</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="p">{</span><span class="n">aRef</span><span class="p">,</span> <span class="n">bRef</span><span class="p">},</span> <span class="p">{},</span> <span class="p">{},</span>
                                  <span class="n">SourceLoc</span><span class="p">(),</span> <span class="cm">/*HasTrailingClosure*/</span> <span class="nb">false</span><span class="p">,</span>
                                  <span class="cm">/*implicit*/</span> <span class="nb">true</span><span class="p">);</span>
  <span class="k">auto</span> <span class="n">switchStmt</span> <span class="o">=</span> <span class="n">SwitchStmt</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">LabeledStmtInfo</span><span class="p">(),</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">abExpr</span><span class="p">,</span>
                                       <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">cases</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">C</span><span class="p">);</span>
  <span class="n">statements</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">switchStmt</span><span class="p">);</span>

  <span class="k">auto</span> <span class="n">body</span> <span class="o">=</span> <span class="n">BraceStmt</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">(),</span> <span class="n">statements</span><span class="p">,</span> <span class="n">SourceLoc</span><span class="p">());</span>
  <span class="n">eqDecl</span><span class="o">-&gt;</span><span class="n">setBody</span><span class="p">(</span><span class="n">body</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>

<p>Now I had to tell the compiler to use this variant for <code>enum</code>s with no <code>case</code>s. Luckily, I know that the <code>EnumDecl</code> had a <code>hasCases()</code> method from the check I removed above. So I just had to plug that in:</p>

<div class="codehilite"><pre><span></span><code><span class="k">auto</span> <span class="n">bodySynthesizer</span> <span class="o">=</span>
    <span class="o">!</span><span class="n">ed</span><span class="o">-&gt;</span><span class="n">hasCases</span><span class="p">()</span>
        <span class="o">?</span> <span class="o">&amp;</span><span class="nl">deriveBodyEquatable_enum_uninhabited_eq</span>
        <span class="p">:</span> <span class="n">ed</span><span class="o">-&gt;</span><span class="n">hasOnlyCasesWithoutAssociatedValues</span><span class="p">()</span>
              <span class="o">?</span> <span class="o">&amp;</span><span class="nl">deriveBodyEquatable_enum_noAssociatedValues_eq</span>
              <span class="p">:</span> <span class="o">&amp;</span><span class="n">deriveBodyEquatable_enum_hasAssociatedValues_eq</span><span class="p">;</span>
<span class="k">return</span> <span class="nf">deriveEquatable_eq</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">TC</span><span class="p">.</span><span class="n">Context</span><span class="p">.</span><span class="n">Id_derived_enum_equals</span><span class="p">,</span>
                          <span class="n">bodySynthesizer</span><span class="p">);</span>
</code></pre></div>

<p>Compiling and running my test file showed that it worked!</p>

<p>From there, I just had to write a test. Luckily, I found an existing test that tested that an <code>enum</code> with no <code>case</code>s wouldn’t derive conformance. I inverted the expectations from that test and had completed my task!</p>

<p>You can see all of this <a href="https://github.com/apple/swift/pull/17756">in my PR</a>.</p>

    ]]></content>
  </entry>

  <entry>
    <title>Entry 3: Unmodified var warnings</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2018-06-27T12:56:34Z</published>
    <updated>2018-06-27T12:56:34Z</updated>
    <content type='html'><![CDATA[
    <p>I’m a sucker for old bugs. I find it especially satisfying to fix something that’s been broken or missing for some time.</p>

<p>Looking through some older type-checker bugs, I found <a href="https://bugs.swift.org/browse/SR-1658">SR-1658</a>:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">func</span> <span class="nf">test</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// This ought to give a warning to change the var to a let</span>
  <span class="kd">var</span> <span class="nv">x</span><span class="p">:</span> <span class="nb">Int</span>
  <span class="k">if</span> <span class="nb">Int</span><span class="p">(</span><span class="s">&quot;abc&quot;</span><span class="p">)</span> <span class="p">==</span> <span class="kc">nil</span> <span class="p">{</span>
    <span class="n">x</span> <span class="p">=</span> <span class="mi">1</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">x</span> <span class="p">=</span> <span class="mi">2</span>
  <span class="p">}</span>
  <span class="bp">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div>

<p>I was really surprised that this didn’t already give a warning. I guess I’ve just assumed that Swift would warn, so I never paid it too much attention. But I played with it a bit more and found that this didn’t give a warning either:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">func</span> <span class="nf">test</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nv">x</span><span class="p">:</span> <span class="nb">Int</span>
  <span class="n">x</span> <span class="p">=</span> <span class="mi">1</span>
  <span class="bp">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div>

<p>Now I was confused and intrigued.</p>

<h2>Trying to understand a bit more</h2>

<p>I did my usual dance to find the source of the warning I expected:</p>

<ol>
<li>Change my test case to get the actual warning</li>
<li>Search the codebase for the text to find the identifier of the diagnostic</li>
<li>Search for the identifier to find where the diagnostic is evoked</li>
</ol>

<p>That led me to <code>VarDeclUsageChecker</code>. That certainly seemed like the right place.</p>

<p>But as I continued to understand what was going on, I decided to also look at uninitialized errors. I <em>knew</em> that it would trigger in the original example. And since both have to track reads and writes to a <code>var</code> or <code>let</code>, I thought they’d be related.</p>

<p>I was wrong. Uninitialized warnings come from <code>LifetimeChecker</code> in <code>swiftSILOptimizer</code>. This is a completely separate library and phase of compilation.</p>

<p>It still seems like these things <em>ought</em> to be done together.</p>

<h2><code>VarDeclUsageChecker</code></h2>

<p>I thought I’d try to press on and fix my issue anyway. Fixing a minor issue would yield an immediate improvement and help me learn what I needed to make a larger change.</p>

<p><code>VarDeclUsageChecker</code> is another AST walker. It visits the different nodes of the AST, building up information. Then, in its destructor, it emits various diagnostics.</p>

<p>AST walkers have a few hooks. Besides the per-node visit functions, there’s also “visit declaration before”, “visit declaration after”, “visit expression before”, “visit expression after”, “visit statement before”, and “visit statement after”. These are called, no matter the type of declaration/expression/statement, before/after the specific visit function is called.</p>

<p>Most of the <code>var</code> usage checking happens in these methods. Each declaration node adds the declaration of each variable to a <code>SmallMapVector</code> (a sorted dictionary) of all the variables that are being tracked.</p>

<p>Each declaration is associated with a bit field.</p>

<div class="codehilite"><pre><span></span><code>  <span class="c1">// Keep track of some information about a variable.</span>
  <span class="k">enum</span> <span class="p">{</span>
    <span class="n">RK_Read</span>        <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>      <span class="c1">///&lt; Whether it was ever read.</span>
    <span class="n">RK_Written</span>     <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>      <span class="c1">///&lt; Whether it was ever written or passed inout.</span>

    <span class="n">RK_CaptureList</span> <span class="o">=</span> <span class="mi">4</span>       <span class="c1">///&lt; Var is an entry in a capture list.</span>
  <span class="p">};</span>
</code></pre></div>

<p>Then the expressions are walked. If a variable is referenced, it’s marked as having been read. If it’s assigned to, it’s marked as being written. If it’s used as an <code>inout</code> reference or a closure capture, then it’s marked as read and written (since you don’t know).</p>

<p>Afterwards, you can emit diagnostics:</p>

<ul>
<li>If a <code>var</code> was never read or written, the declaration can be deleted</li>
<li>If a <code>var</code> was only written once, it can be a <code>let</code></li>
</ul>

<p>A few other caveats also apply. e.g. if you do <code>var (x, y, z) = …</code> and only <code>y</code> is mutated, no warning is given to use <code>let</code> for <code>x</code> and <code>z</code>.</p>

<h2>Trying to fix the issue</h2>

<p>Having understood the basics of how this worked, I thought I’d be able to improve the diagnostic by adding another flag: <code>RK_Initialized</code>.</p>

<div class="codehilite"><pre><span></span><code><span class="kd">func</span> <span class="nf">test</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nv">x</span><span class="p">:</span> <span class="nb">Int</span>
  <span class="n">x</span> <span class="p">=</span> <span class="mi">1</span>
  <span class="bp">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div>

<p>The <code>x = 1</code> here is initializing the variable, but it’s counted as a write. By separating writes and initialization, the compiler could detect the above case and still suggest a <code>let</code>.</p>

<p>I added <code>RK_Initialized</code> and looked for the correct place to start using it. I figured my change would have 2 parts:</p>

<ol>
<li>Start the variable as initialized if it’s declared with a value</li>
<li>Change the first <em>written</em> to <em>initialized</em> if it wasn’t already <em>initialized</em></li>
</ol>

<p>I started by trying to set the initialized flag on declaration using this code:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nv">y</span><span class="p">:</span> <span class="nb">Int</span> <span class="p">=</span> <span class="mi">1</span>
  <span class="bp">print</span><span class="p">(</span><span class="n">y</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div>

<p>A look at the AST showed what I should be looking for:</p>

<pre><code>(pattern_binding_decl
  (pattern_typed type='Int'
    (pattern_named type='Int' 'y')
    (type_ident
      (component id='Int' bind=Swift.(file).Int)))
  (call_expr implicit type='Int' location=1658.swift:6:15 range=[1658.swift:6:15 - line:6:15] nothrow arg_labels=_builtinIntegerLiteral:
    (constructor_ref_call_expr implicit type='(_MaxBuiltinIntegerType) -&gt; Int' location=1658.swift:6:15 range=[1658.swift:6:15 - line:6:15] nothrow
      (declref_expr implicit type='(Int.Type) -&gt; (_MaxBuiltinIntegerType) -&gt; Int' location=1658.swift:6:15 range=[1658.swift:6:15 - line:6:15] decl=Swift.(file).Int.init(_builtinIntegerLiteral:) function_ref=single)
      (type_expr implicit type='Int.Type' location=1658.swift:6:15 range=[1658.swift:6:15 - line:6:15] typerepr='Int'))
    (tuple_expr implicit type='(_builtinIntegerLiteral: Int2048)' location=1658.swift:6:15 range=[1658.swift:6:15 - line:6:15] names=_builtinIntegerLiteral
      (integer_literal_expr type='Int2048' location=1658.swift:6:15 range=[1658.swift:6:15 - line:6:15] value=1))))

(var_decl "y" type='Int' interface type='Int' access=private storage_kind=stored)
</code></pre>

<p>Variable declarations happen within a pattern binding, which contains 1 or more assignments.</p>

<p>The code in <code>walkToDeclPre</code> actually noted that these were explicitly ignored:</p>

<div class="codehilite"><pre><span></span><code>    <span class="c1">// Note that we ignore the initialization behavior of PatternBindingDecls,</span>
    <span class="c1">// but we do want to walk into them, because we want to see any uses or</span>
    <span class="c1">// other things going on in the initializer expressions.</span>
</code></pre></div>

<p>I deleted the comment and found some other code the looked at <code>PatternBindingDecl</code>s. I used what I found there to help me mark initialized variables as initialized:</p>

<div class="codehilite"><pre><span></span><code>    <span class="c1">// Look for the pattern binding declarations</span>
    <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="o">*</span><span class="n">pbd</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">PatternBindingDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">D</span><span class="p">))</span> <span class="p">{</span>
      <span class="c1">// Look at each item in the binding. `let (x, y) = (1, 2), z: Int` has</span>
      <span class="c1">// 2 items: `(x, y)` and `z`.</span>
      <span class="k">for</span> <span class="p">(</span><span class="n">PatternBindingEntry</span> <span class="nl">pbe</span> <span class="p">:</span> <span class="n">pbd</span><span class="o">-&gt;</span><span class="n">getPatternList</span><span class="p">())</span> <span class="p">{</span>
        <span class="c1">// We only care about bindings that have an init since we&#39;re</span>
        <span class="c1">// specifically trying to mark them as initialized</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pbe</span><span class="p">.</span><span class="n">getInit</span><span class="p">())</span>
          <span class="k">continue</span><span class="p">;</span>

        <span class="c1">// Mark each variable in the entry (`x` and `y`) as initialized</span>
        <span class="n">pbe</span><span class="p">.</span><span class="n">getPattern</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">forEachVariable</span><span class="p">([</span><span class="o">&amp;</span><span class="p">](</span><span class="n">VarDecl</span> <span class="o">*</span><span class="n">VD</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">VarDecls</span><span class="p">[</span><span class="n">VD</span><span class="p">]</span> <span class="o">|=</span> <span class="n">RK_Initialized</span><span class="p">;</span>
        <span class="p">});</span>
      <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div>

<p>Next, I needed to track the first write as an initialization. This was easy to hack together. All the reads and writes funnel through a central method. I modified it to mark the first write as an initialization instead:</p>

<div class="codehilite"><pre><span></span><code>  <span class="kt">void</span> <span class="nf">addMark</span><span class="p">(</span><span class="n">Decl</span> <span class="o">*</span><span class="n">D</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">Flag</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">auto</span> <span class="o">*</span><span class="n">vd</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">VarDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">D</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">vd</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>

    <span class="k">auto</span> <span class="n">vdi</span> <span class="o">=</span> <span class="n">VarDecls</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">vd</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">vdi</span> <span class="o">!=</span> <span class="n">VarDecls</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">((</span><span class="n">Flag</span> <span class="o">&amp;</span> <span class="n">RK_Written</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span><span class="n">vdi</span><span class="o">-&gt;</span><span class="n">second</span> <span class="o">&amp;</span> <span class="n">RK_Initialized</span><span class="p">))</span>
        <span class="n">vdi</span><span class="o">-&gt;</span><span class="n">second</span> <span class="o">|=</span> <span class="p">(</span><span class="n">Flag</span> <span class="o">&amp;</span> <span class="o">~</span><span class="n">RK_Written</span><span class="p">)</span> <span class="o">|</span> <span class="n">RK_Initialized</span><span class="p">;</span>
      <span class="k">else</span>
        <span class="n">vdi</span><span class="o">-&gt;</span><span class="n">second</span> <span class="o">|=</span> <span class="n">Flag</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
</code></pre></div>

<p>This wasn’t very clean, but I wanted to focus on a proof-of-concept first. I’d clean it up later, if it worked.</p>

<p>I quickly discovered that I’d also need to check <code>!(Flag &amp; RK_Read)</code>. When a variable is passed as <code>inout</code> to a function, it’s marked as both read and write—since the compiler doesn’t know what the function will do. In those cases, I needed to preserve the <code>RK_Written</code> mark.</p>

<p>Lastly, I needed to update a few other variable declarations to include the initialized flag. E.g., if you have a top-level variable declaration in a file, that declaration was handled here:</p>

<div class="codehilite"><pre><span></span><code>    <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="o">*</span><span class="n">TLCD</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">TopLevelCodeDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">D</span><span class="p">))</span> <span class="p">{</span>
      <span class="c1">// If this is a TopLevelCodeDecl, scan for global variables</span>
      <span class="k">auto</span> <span class="o">*</span><span class="n">body</span> <span class="o">=</span> <span class="n">TLCD</span><span class="o">-&gt;</span><span class="n">getBody</span><span class="p">();</span>
      <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">node</span> <span class="p">:</span> <span class="n">body</span><span class="o">-&gt;</span><span class="n">getElements</span><span class="p">())</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">node</span><span class="p">.</span><span class="n">is</span><span class="o">&lt;</span><span class="n">Decl</span> <span class="o">*&gt;</span><span class="p">())</span> <span class="p">{</span>
          <span class="c1">// Flag all variables in a PatternBindingDecl</span>
          <span class="n">Decl</span> <span class="o">*</span><span class="n">D</span> <span class="o">=</span> <span class="n">node</span><span class="p">.</span><span class="n">get</span><span class="o">&lt;</span><span class="n">Decl</span> <span class="o">*&gt;</span><span class="p">();</span>
          <span class="k">auto</span> <span class="o">*</span><span class="n">PBD</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">PatternBindingDecl</span><span class="o">&gt;</span><span class="p">(</span><span class="n">D</span><span class="p">);</span>
          <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">PBD</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>
          <span class="k">for</span> <span class="p">(</span><span class="n">PatternBindingEntry</span> <span class="nl">PBE</span> <span class="p">:</span> <span class="n">PBD</span><span class="o">-&gt;</span><span class="n">getPatternList</span><span class="p">())</span> <span class="p">{</span>
            <span class="n">PBE</span><span class="p">.</span><span class="n">getPattern</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">forEachVariable</span><span class="p">([</span><span class="o">&amp;</span><span class="p">](</span><span class="n">VarDecl</span> <span class="o">*</span><span class="n">VD</span><span class="p">)</span> <span class="p">{</span>
              <span class="n">VarDecls</span><span class="p">[</span><span class="n">VD</span><span class="p">]</span> <span class="o">=</span> <span class="n">RK_Initialized</span><span class="o">|</span><span class="n">RK_Read</span><span class="o">|</span><span class="n">RK_Written</span><span class="p">;</span>
            <span class="p">});</span>
          <span class="p">}</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">node</span><span class="p">.</span><span class="n">is</span><span class="o">&lt;</span><span class="n">Stmt</span> <span class="o">*&gt;</span><span class="p">())</span> <span class="p">{</span>
          <span class="c1">// Flag all variables in guard statements</span>
          <span class="n">Stmt</span> <span class="o">*</span><span class="n">S</span> <span class="o">=</span> <span class="n">node</span><span class="p">.</span><span class="n">get</span><span class="o">&lt;</span><span class="n">Stmt</span> <span class="o">*&gt;</span><span class="p">();</span>
          <span class="k">auto</span> <span class="o">*</span><span class="n">GS</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">GuardStmt</span><span class="o">&gt;</span><span class="p">(</span><span class="n">S</span><span class="p">);</span>
          <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">GS</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>
          <span class="k">for</span> <span class="p">(</span><span class="n">StmtConditionElement</span> <span class="nl">SCE</span> <span class="p">:</span> <span class="n">GS</span><span class="o">-&gt;</span><span class="n">getCond</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">pattern</span> <span class="o">=</span> <span class="n">SCE</span><span class="p">.</span><span class="n">getPatternOrNull</span><span class="p">())</span> <span class="p">{</span>
              <span class="n">pattern</span><span class="o">-&gt;</span><span class="n">forEachVariable</span><span class="p">([</span><span class="o">&amp;</span><span class="p">](</span><span class="n">VarDecl</span> <span class="o">*</span><span class="n">VD</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">VarDecls</span><span class="p">[</span><span class="n">VD</span><span class="p">]</span> <span class="o">=</span> <span class="n">RK_Read</span><span class="o">|</span><span class="n">RK_Initialized</span><span class="p">;</span>
              <span class="p">});</span>
            <span class="p">}</span>
          <span class="p">}</span>
        <span class="p">}</span>
      <span class="p">}</span>
</code></pre></div>

<p>After verifying that my test case emitted the warning I wanted, I started working through the test suite. Running it turned up quite a few failures, but many of them were legitimate! That was good news: the warning would be useful.</p>

<p>Unfortunately, I discovered some test cases that I couldn’t handle:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">var</span> <span class="nv">s</span> <span class="p">:</span> <span class="nb">String</span>
<span class="k">for</span> <span class="kc">_</span> <span class="k">in</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="p">{</span>
    <span class="n">s</span> <span class="p">=</span> <span class="s">&quot; &quot;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">s</span>
</code></pre></div>

<p>In this case, the write serves as both a write and as initialization—because it’s inside a loop.</p>

<p>To correctly detect this, I’d need to know if any loops existed between the declaration of the variable and the write. I haven’t figured out a good way to do this yet.</p>

<h2>Regrouping</h2>

<p>To recap, I thought it’d be easier to warn in this case:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">var</span> <span class="nv">x</span><span class="p">:</span> <span class="nb">Int</span>
<span class="n">x</span> <span class="p">=</span> <span class="mi">1</span>
<span class="bp">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
</code></pre></div>

<p>But it turned out to be almost as hard as the case described in the bug:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">var</span> <span class="nv">x</span><span class="p">:</span> <span class="nb">Int</span>
<span class="k">if</span> <span class="nb">Int</span><span class="p">(</span><span class="s">&quot;abc&quot;</span><span class="p">)</span> <span class="p">==</span> <span class="kc">nil</span> <span class="p">{</span>
  <span class="n">x</span> <span class="p">=</span> <span class="mi">1</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="n">x</span> <span class="p">=</span> <span class="mi">2</span>
<span class="p">}</span>
<span class="bp">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
</code></pre></div>

<p>So I think it’s worth considering how to solve the general case before proceeding. I mentioned above that uninitialized variable errors are handled elsewhere:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">var</span> <span class="nv">x</span><span class="p">:</span> <span class="nb">Int</span>
<span class="k">if</span> <span class="nb">Int</span><span class="p">(</span><span class="s">&quot;abc&quot;</span><span class="p">)</span> <span class="p">==</span> <span class="kc">nil</span> <span class="p">{</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="n">x</span> <span class="p">=</span> <span class="mi">2</span>
<span class="p">}</span>
<span class="bp">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="c1">// This correctly errors because x wasn&#39;t set</span>
</code></pre></div>

<p>It seems to me like those two bits of code should be combined. Both are tracking reads and writes to variables and emitting diagnostics. But since I’m not certain of this, <a href="https://forums.swift.org/t/combine-definite-initialization-and-variable-usage-checks/14030">I’ve asked on the forums</a>. Hopefully that will provide some helpful direction for the future.</p>

    ]]></content>
  </entry>

  <entry>
    <title>Entry 2: SR-7177, Adding a diagnostic</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2018-06-15T22:14:20Z</published>
    <updated>2018-06-15T22:14:20Z</updated>
    <content type='html'><![CDATA[
    <p>In <a href="/swift/0">Entry 0</a> I pattern matched to add a missing constraint to a KeyPath subscript. In <a href="/swift/1">Entry 1</a> I improved an error message. Now it’s time to put combine my skills to really improve a diagnostic (a term that encompasses both errors and warnings).</p>

<p>This one was a bit of a ride.</p>

<h2>Starting point</h2>

<p><a href="https://bugs.swift.org/browse/SR-7177">SR-7177</a> was filed because of a confusing diagnostic. At some point, that diagnostic improved. But it was still a bit confusing:</p>

<pre><code>var a = [1,2,3,4]
var b = a.popFirst()

error: '[Int]' requires the types '[Int]' and 'ArraySlice&lt;Int&gt;' be equivalent to use 'popFirst'
var b = a.popFirst()
          ^
</code></pre>

<p>Uh… okay? Who does <code>[Int]</code> think it is? And why does it require <code>[Int]</code> and <code>ArraySlice&lt;Int&gt;</code> be equivalent? Those will never be equivalent. Where does this <code>popFirst</code> come from?</p>

<p>I was genuinely confused by this one. Looking up the documentation for <code>popFirst()</code> didn’t turn up anything useful, although I did find that it was defined on <code>Collection</code>. (I’m not the only one who can’t keep all the these protocols straight, right?)</p>

<p>But looking up the definition of <code>popFirst()</code> cleared things up:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">extension</span> <span class="nc">Collection</span> <span class="k">where</span> <span class="kc">Self</span> <span class="p">==</span> <span class="kc">Self</span><span class="p">.</span><span class="n">SubSequence</span> <span class="p">{</span>
    <span class="c1">/// Removes and returns the first element of the collection.</span>
    <span class="c1">///</span>
    <span class="c1">/// - Returns: The first element of the collection if the collection is</span>
    <span class="c1">///   not empty; otherwise, `nil`.</span>
    <span class="c1">///</span>
    <span class="c1">/// - Complexity: O(1)</span>
    <span class="kd">public</span> <span class="kr">mutating</span> <span class="kd">func</span> <span class="nf">popFirst</span><span class="p">()</span> <span class="p">-&gt;</span> <span class="kc">Self</span><span class="p">.</span><span class="n">Element</span><span class="p">?</span>
<span class="p">}</span>
</code></pre></div>

<p>You can’t use <code>popFirst</code> on an <code>Array</code> because <code>Array.SubSequence == ArraySlice</code>. It sure would be nice if the diagnostic would say that!</p>

<h2>Finding the diagnostic</h2>

<p>The first step was to find the existing diagnostic. A quick search for <em>be equivalent to use</em> turned up the definition in <code>DiagnosticsSema.def</code>:</p>

<pre><code>ERROR(types_not_equal_in_call,none,
      "%0 requires the types %1 and %2 be equivalent to use %3",
      (Type, Type, Type, DeclName))
</code></pre>

<p>Searching for that id, <code>types_not_equal_in_call</code>, pinpointed where this was called in <code>CSDiag.cpp</code> (the file where diagnostics are created from the constraint solver) inside a function called <code>diagnoseTypeRequirementFailure</code>.</p>

<p>This function is used to diagnose errors related to type requirements (duh!). In other words, errors related to <code>where</code> clauses.</p>

<p>Inside is this code, which chooses a diagnostic based on the type of failed constraint in the clause:</p>

<div class="codehilite"><pre><span></span><code>  <span class="k">switch</span> <span class="p">(</span><span class="n">constraint</span><span class="o">-&gt;</span><span class="n">getKind</span><span class="p">())</span> <span class="p">{</span>
  <span class="k">case</span> <span class="n">ConstraintKind</span><span class="o">::</span><span class="nl">ConformsTo</span><span class="p">:</span>
    <span class="n">TC</span><span class="p">.</span><span class="n">diagnose</span><span class="p">(</span><span class="n">anchor</span><span class="o">-&gt;</span><span class="n">getLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">type_does_not_conform_owner</span><span class="p">,</span> <span class="n">ownerType</span><span class="p">,</span>
                <span class="n">lhs</span><span class="p">,</span> <span class="n">rhs</span><span class="p">);</span>
    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>

  <span class="k">case</span> <span class="n">ConstraintKind</span><span class="o">::</span><span class="nl">Subtype</span><span class="p">:</span> <span class="c1">// superclass</span>
    <span class="n">TC</span><span class="p">.</span><span class="n">diagnose</span><span class="p">(</span><span class="n">anchor</span><span class="o">-&gt;</span><span class="n">getLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">type_does_not_inherit</span><span class="p">,</span> <span class="n">ownerType</span><span class="p">,</span> <span class="n">lhs</span><span class="p">,</span>
                <span class="n">rhs</span><span class="p">);</span>
    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>

  <span class="k">case</span> <span class="n">ConstraintKind</span><span class="o">::</span><span class="nl">Equal</span><span class="p">:</span> <span class="p">{</span> <span class="c1">// same type</span>
    <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="o">*</span><span class="n">UDE</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">UnresolvedDotExpr</span><span class="o">&gt;</span><span class="p">(</span><span class="n">anchor</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">TC</span><span class="p">.</span><span class="n">diagnose</span><span class="p">(</span><span class="n">UDE</span><span class="o">-&gt;</span><span class="n">getLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">types_not_equal_in_call</span><span class="p">,</span> <span class="n">ownerType</span><span class="p">,</span> <span class="n">lhs</span><span class="p">,</span>
                  <span class="n">rhs</span><span class="p">,</span> <span class="n">UDE</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">());</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">TC</span><span class="p">.</span><span class="n">diagnose</span><span class="p">(</span><span class="n">anchor</span><span class="o">-&gt;</span><span class="n">getLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">types_not_equal</span><span class="p">,</span> <span class="n">ownerType</span><span class="p">,</span> <span class="n">lhs</span><span class="p">,</span> <span class="n">rhs</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
  <span class="p">}</span>
</code></pre></div>

<p>Since this is a <code>==</code> constraint, the last <code>case</code> is used. We can see that it already does a little pattern matching. If it’s an unresolved dot expression—something that uses a method or property or an unknown (hence unresolved) type—then the error includes the name of the method.</p>

<p>That matches what we saw:</p>

<pre><code>error: '[Int]' requires the types '[Int]' and 'ArraySlice&lt;Int&gt;' be equivalent to use 'popFirst'
var b = a.popFirst()
          ^
</code></pre>

<h2>What would a better error be?</h2>

<p>I thought this would ideally match the definition as much as possible. Perhaps:</p>

<pre><code>error: 'Collection' requires the types 'Self' ('[Int]') and 'Self.SubSequence' ('ArraySlice&lt;Int&gt;') be equivalent to use 'popFirst'
var b = a.popFirst()
          ^
</code></pre>

<p>And in many places, Swift will give you “note” that tells you where a method was declared. So ideally this would include a note like this too:</p>

<pre><code>Swift.Collection:2:37: note: 'popFirst()' declared here
    @inlinable public mutating func popFirst() -&gt; Self.Element?
                                    ^
</code></pre>

<p>Then I wouldn’t have had to hunt around for the definition. I (hopefully) could just click on it in Xcode.</p>

<h3>Attempt #1</h3>

<p>I started by trying to add the note, since I thought I could figure that out more easily. And since it’s always easiest to copy existing code, I tried to find the source of one of the existing <em>declared here</em> notes—mostly because I couldn’t remember the exact text.</p>

<pre><code>Untitled.swift:1:5: error: value of type 'String' has no member 'coun'; did you mean 'count'?
_ = "".coun()
    ^~ ~~~~
       count
Swift.String:10:16: note: 'count' declared here
    public var count: Int { get }
               ^
Swift.Collection:5:16: note: 'count' declared here
    public var count: Int { get }
               ^
</code></pre>

<p>Bingo.</p>

<p>Searching back in <code>DiagnosticsSema.def</code> for <code>declared here</code> turned up a number of results. Most of them either lacked a format parameter or, based on the name, were clearly not what I was looking for. But <code>decl_declared_here</code> looked promising:</p>

<pre><code>NOTE(decl_declared_here,none,
     "%0 declared here", (DeclName))
</code></pre>

<p>Searching turned up a lot of usages like this:</p>

<pre><code>tc.diagnose(func, diag::decl_declared_here, func-&gt;getFullName());
</code></pre>

<p>So what was a <code>DeclName</code> and how could I get one?</p>

<p>Since the <code>types_not_equal_in_call</code> diagnostic that I was trying to improve also passed a <code>DeclName</code>, I tried copying that.</p>

<div class="codehilite"><pre><span></span><code><span class="n">TC</span><span class="p">.</span><span class="n">diagnose</span><span class="p">(</span><span class="n">UDE</span><span class="o">-&gt;</span><span class="n">getLoc</span><span class="p">(),</span> <span class="n">diag</span><span class="o">::</span><span class="n">decl_declared_here</span><span class="p">,</span> <span class="n">UDE</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">());</span>
</code></pre></div>

<p>But that printed a note that pointed to the line that was <em>using</em> <code>popFirst</code>, which was neither true nor helpful. 🙃</p>

<pre><code>note: 'popFirst' declared here
var b = a.popFirst()
          ^
</code></pre>

<p><code>UDE</code> is an <code>UnresolvedDotExpr</code>—a method call where the type wasn’t known. But I knew that the type <em>was</em> known. If it wasn’t, then we wouldn’t get an error about the type requirements.</p>

<p>At first, I thought I might be able to get this information from the type parameter constraint. But dumping the constraint system showed where that information resided:</p>

<pre><code>Score: 0 0 0 0 0 0 0 0 0 0 0
  Fix-it applied, fixed expression was:
Type Variables:
  $T0 [lvalue allowed] as @lvalue [Int] @ locator@0x11d087600 [DeclRef@7177.swift:2:9]
  $T1 [lvalue allowed] as () -&gt; Int? @ locator@0x11d087680 [UnresolvedDot@7177.swift:2:11 -&gt; member]
  $T2 as [Int] @ locator@0x11d0876e0 [UnresolvedDot@7177.swift:2:11 -&gt; member -&gt; archetype 'Self']

Active Constraints:

Inactive Constraints:
Resolved overloads:
  selected overload set choice @lvalue [Int].popFirst: $T1 == () -&gt; $T2.Element?
  selected overload set choice a: $T0 == @lvalue [Int]


Opened types:
    cs.dump()
  locator@0x11d087680 [UnresolvedDot@7177.swift:2:11 -&gt; member] opens τ_0_0 -&gt; $T2

Failed constraint:
  $T2 equal $T2.SubSequence [[locator@0x11d0877f8 [UnresolvedDot@7177.swift:2:11 -&gt; member -&gt; opened generic -&gt; type parameter requirement #1]]];
</code></pre>

<p>The unresolved dot expression had a resolved overload in the constraint system. I just needed to figure out how to get it.</p>

<p>I searched in the <code>dump</code> method and found that that list was part of <code>ConstraintSystem::resolvedOverloadSets</code>. Searching for use of that turned up the <code>ConstraintSystem::findResolvedMemberRef</code> method, which seemed to do just what I wanted.</p>

<p>My first attempt to actually use it failed—the resolved member wasn’t found. I figured this was because I was passing in the wrong <code>ConstraintLocator</code>. The <code>ConstraintLocator</code> I was working with looked like this:</p>

<pre><code>(lldb) e locator-&gt;dump(&amp;cs)
locator@0x117187bf8 [UnresolvedDot@7177.swift:2:11 -&gt; member -&gt; opened generic -&gt; type parameter requirement #1]
</code></pre>

<p>Each locator is made up of an _anchor—the expression it’s from—and a list of <code>PathElement</code>s. Those elements show where the constraint came from.</p>

<p>Searching for other uses of <code>findResolvedMemberRef</code> showed that you can ask for a new <code>ConstraintLocator</code> with a specific type of path element:</p>

<div class="codehilite"><pre><span></span><code>  <span class="k">auto</span> <span class="n">loc</span> <span class="o">=</span> <span class="n">cs</span><span class="p">.</span><span class="n">getConstraintLocator</span><span class="p">(</span><span class="n">UDE</span><span class="p">,</span> <span class="n">ConstraintLocator</span><span class="o">::</span><span class="n">Member</span><span class="p">);</span>
  <span class="k">auto</span> <span class="o">*</span><span class="n">member</span> <span class="o">=</span> <span class="n">cs</span><span class="p">.</span><span class="n">findResolvedMemberRef</span><span class="p">(</span><span class="n">loc</span><span class="p">);</span>
</code></pre></div>

<p><code>member</code> here is a <code>ValueDecl</code>, which I could pass as the source of the note so that it appeared on the declaration of the method.</p>

<p>Now that I had the note in place, I had to figure out how to and whether I could show the types that I wanted. With my debugger parked on the existing diagnostic, I started exploring the types I had access to.</p>

<p>By jumping through the headers, I discovered that <code>member</code> had access to the name of the type it was defined on. That makes sense: <code>Collection.popFirst</code> should know that it’s defined on <code>Collection</code>.</p>

<pre><code>(lldb) e member-&gt;getDeclContext()-&gt;getDeclaredInterfaceType()-&gt;dump()
(protocol_type decl=Swift.(file).Collection)
</code></pre>

<p>Now I could replace the first type in the diagnostic with that.</p>

<pre><code>error: '[Int]' requires the types '[Int]' and 'ArraySlice&lt;Int&gt;' be equivalent to use 'popFirst'
var b = a.popFirst()
          ^
</code></pre>

<p>But I still wanted to show that this was from the <code>Self == Self.SubSequence</code> constraint. That constraint was available in the diagnostic method. Dumping the constraint’s types to the console gave me a clue of where to go next:</p>

<pre><code>(lldb) e constraint-&gt;getFirstType()-&gt;dump()
(type_variable_type id=2)
(lldb) e constraint-&gt;getSecondType()-&gt;dump()
(dependent_member_type assoc_type=Swift.(file).Sequence.SubSequence
  (base=type_variable_type id=2))
</code></pre>

<p>The constraint was of the form <code>$T2 == $T2.SubSequence</code> because the solver was figuring out the concrete <code>Collection</code> type that it should use. I searched for <code>dependent_member_type</code> to see what that was and discovered <code>DependentMemberType</code> (a straightforward translation in retrospect).</p>

<p><code>DependentMemberType</code> has a base type and either a name or associated type.</p>

<div class="codehilite"><pre><span></span><code><span class="c1">/// A type that refers to a member type of some type that is dependent on a</span>
<span class="c1">/// generic parameter.</span>
<span class="k">class</span> <span class="nc">DependentMemberType</span> <span class="o">:</span> <span class="k">public</span> <span class="n">TypeBase</span> <span class="p">{</span>
  <span class="n">Type</span> <span class="n">Base</span><span class="p">;</span>
  <span class="n">llvm</span><span class="o">::</span><span class="n">PointerUnion</span><span class="o">&lt;</span><span class="n">Identifier</span><span class="p">,</span> <span class="n">AssociatedTypeDecl</span> <span class="o">*&gt;</span> <span class="n">NameOrAssocType</span><span class="p">;</span>

  <span class="err">…</span>
<span class="p">}</span>
</code></pre></div>

<p>That seemed like what I’d want to include with the diagnostic. But I didn’t want to show <code>$T2.SubSequence</code>, I wanted to show <code>[Int].SubSequence</code>. So I created a new <code>DependentMemberType</code> that used the <code>[Int]</code> type I had with the <code>.SubSequence</code> from the existing <code>DependentMemberType</code>:</p>

<div class="codehilite"><pre><span></span><code><span class="k">auto</span> <span class="n">rhsType</span> <span class="o">=</span> <span class="n">constraint</span><span class="o">-&gt;</span><span class="n">getSecondType</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getAs</span><span class="o">&lt;</span><span class="n">DependentMemberType</span><span class="o">&gt;</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">rhsType</span><span class="p">)</span>
  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="k">auto</span> <span class="n">memberType</span> <span class="o">=</span> <span class="n">DependentMemberType</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">lhs</span><span class="p">,</span> <span class="n">rhsType</span><span class="o">-&gt;</span><span class="n">getAssocType</span><span class="p">());</span>
</code></pre></div>

<p>Adding a new diagnostic that included the extra type gave me a reasonable diagnostic.</p>

<pre><code>error: 'Collection' requires the types '[Int]' and 'Array&lt;Int&gt;.SubSequence' ('ArraySlice&lt;Int&gt;') be equivalent to use 'popFirst'
var b = a.popFirst()
          ^
Swift.Collection:2:37: note: 'popFirst()' declared here
    @inlinable public mutating func popFirst() -&gt; Self.Element?
                                    ^
</code></pre>

<p>I was pretty happy with that, so I opened <a href="https://github.com/apple/swift/pull/17210">a PR</a></p>

<h2>Attempt #2</h2>

<p>When I opened a PR with the above diagnostic, <a href="https://github.com/xedin">Pavel Yaskevich</a> pointed out that Swift already had a similar diagnostic that would be clearer:</p>

<div class="codehilite"><pre><span></span><code><span class="kd">func</span> <span class="nf">countOf</span><span class="p">&lt;</span><span class="n">C</span><span class="p">:</span> <span class="n">Collection</span><span class="p">&gt;(</span><span class="kc">_</span> <span class="n">c</span><span class="p">:</span> <span class="n">C</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="k">where</span> <span class="n">C</span> <span class="p">==</span> <span class="n">C</span><span class="p">.</span><span class="n">SubSequence</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">c</span><span class="p">.</span><span class="bp">count</span>
<span class="p">}</span>
<span class="kc">_</span> <span class="p">=</span> <span class="n">countOf</span><span class="p">([</span><span class="mi">1</span><span class="p">])</span>

<span class="n">error</span><span class="p">:</span> <span class="n">cannot</span> <span class="n">invoke</span> <span class="err">&#39;</span><span class="n">countOf</span><span class="p">(</span><span class="kc">_</span><span class="p">:)</span><span class="err">&#39;</span> <span class="n">with</span> <span class="n">an</span> <span class="n">argument</span> <span class="n">list</span> <span class="n">of</span> <span class="n">type</span> <span class="err">&#39;</span><span class="p">([</span><span class="nb">Int</span><span class="p">])</span><span class="err">&#39;</span>
<span class="kc">_</span> <span class="p">=</span> <span class="n">countOf</span><span class="p">([</span><span class="mi">1</span><span class="p">])</span>
            <span class="o">^</span>
<span class="n">note</span><span class="p">:</span> <span class="n">candidate</span> <span class="n">requires</span> <span class="n">that</span> <span class="n">the</span> <span class="n">types</span> <span class="err">&#39;</span><span class="p">[</span><span class="nb">Int</span><span class="p">]</span><span class="err">&#39;</span> <span class="n">and</span> <span class="err">&#39;</span><span class="n">ArraySlice</span><span class="p">&lt;</span><span class="nb">Int</span><span class="p">&gt;</span><span class="err">&#39;</span> <span class="n">be</span> <span class="n">equivalent</span> <span class="p">(</span><span class="n">requirement</span> <span class="n">specified</span> <span class="k">as</span> <span class="err">&#39;</span><span class="n">C</span><span class="err">&#39;</span> <span class="p">==</span> <span class="err">&#39;</span><span class="n">C</span><span class="p">.</span><span class="n">SubSequence</span><span class="err">&#39;</span> <span class="p">[</span><span class="n">with</span> <span class="n">C</span> <span class="p">=</span> <span class="p">[</span><span class="nb">Int</span><span class="p">]])</span>
<span class="kd">func</span> <span class="nf">countOf</span><span class="p">&lt;</span><span class="n">C</span><span class="p">:</span> <span class="n">Collection</span><span class="p">&gt;(</span><span class="kc">_</span> <span class="n">c</span><span class="p">:</span> <span class="n">C</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nb">Int</span> <span class="k">where</span> <span class="n">C</span> <span class="p">==</span> <span class="n">C</span><span class="p">.</span><span class="n">SubSequence</span> <span class="p">{</span>
     <span class="o">^</span>
</code></pre></div>

<p>So back I went to the code. I found where the diagnostic was generated and saw that it used a <code>Requirement</code>. That was a new type to me. It’s the AST-equivalent of the <code>Constraint</code> I used above—it represents <code>C == C.SubSequence</code> in the code above. From that <code>Requirement</code>, a <code>Constraint</code> is generated.</p>

<p>Unfortunately, I didn’t see how to work back from the <code>Constraint</code> to the <code>Requirement</code>. But Pavel very helpfully told me that I could get there from the <code>ConstraintLocator</code>.</p>

<div class="codehilite"><pre><span></span><code><span class="k">auto</span> <span class="n">path</span> <span class="o">=</span> <span class="n">locator</span><span class="o">-&gt;</span><span class="n">getPath</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">path</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span>
  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

<span class="k">auto</span> <span class="o">&amp;</span><span class="n">last</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="n">back</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">last</span><span class="p">.</span><span class="n">getKind</span><span class="p">()</span> <span class="o">!=</span> <span class="n">ConstraintLocator</span><span class="o">::</span><span class="n">TypeParameterRequirement</span><span class="p">)</span>
  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

<span class="k">auto</span> <span class="n">req</span> <span class="o">=</span> <span class="n">member</span>
  <span class="o">-&gt;</span><span class="n">getAsGenericContext</span><span class="p">()</span>
  <span class="o">-&gt;</span><span class="n">getGenericSignature</span><span class="p">()</span>
  <span class="o">-&gt;</span><span class="n">getRequirements</span><span class="p">()[</span><span class="n">last</span><span class="p">.</span><span class="n">getValue</span><span class="p">()];</span>
</code></pre></div>

<p>Remember the constraint locator I mentioned above?</p>

<pre><code>(lldb) e locator-&gt;dump(&amp;cs)
locator@0x117187bf8 [UnresolvedDot@7177.swift:2:11 -&gt; member -&gt; opened generic -&gt; type parameter requirement #1]
</code></pre>

<p>The last item in the path is the type parameter requirement at index 1. By looking up the requirements for the generic signature of the <code>popFirst</code> method, I could use that index to get the requirement I was after.</p>

<p>Then I was able look up the diagnostic that Pavel pointed me to and use its code to create a similar diagnostic note here. And since the note pointed out that the <em>candidate</em> had an un-met requirement, I realized that the more general “has no member” error could be used.</p>

<pre><code>error: value of type '[Int]' has no member 'popFirst'
var b = a.popFirst()
          ^
Swift.Collection:2:37: note: candidate requires that the types '[Int]' and 'ArraySlice&lt;Int&gt;' be equivalent (requirement specified as 'Self' == 'Self.SubSequence')
    @inlinable public mutating func popFirst() -&gt; Self.Element?
                                    ^
</code></pre>

<p>I like that even more than my first error. Consistency between error messages is definitely helpful.</p>

<p>This attempt <em>seems</em> much easier—my description of it is at least much shorter—but it was actually more difficult for me to figure out. That’s in part because I used what I learned in the first attempt. But it also makes sense: I had to learn some new things to do things this way.</p>

<h2>Detour: why did it spell out <em>Array</em> in my first attempt?</h2>

<p>I was surprised to see that the error from my first attempt specified <code>[Int]</code> but <code>Array&lt;Int&gt;.SubSequence</code> instead of <code>[Int].SubSequence</code>, which I would have preferred since the connection is clearer.</p>

<pre><code>error: 'Collection' requires the types '[Int]' and 'Array&lt;Int&gt;.SubSequence' ('ArraySlice&lt;Int&gt;') be equivalent to use 'popFirst'
var b = a.popFirst()
          ^
</code></pre>

<p>My first attempt to figure this out was to “Jump to Definition” in Xcode on the <code>diagnose</code> method. That didn’t lead anywhere useful since that method uses template magic.</p>

<p>So I resorted to searching for <code>'.'</code> and setting breakpoints. My search turned up a promising result in <code>DiagnosticEngine.cpp</code>, but the breakpoint wasn’t hit. Since I was pretty sure that <code>DiagnosticEngine::emitDiagnostic</code> was the correct method, I set a breakpoint at the beginning of the method.</p>

<p>Stepping through, nothing exciting happened until the end. I eventually realized that I should step into <code>Consumer-&gt;handleDiagnostic</code> since it took a list of arguments. I wasn’t sure whether the arguments would already be stringified at this point, so I kept stepping through.</p>

<p>This eventually lead me to the <code>formatDiagnosticArgument</code> function in <code>DiagnosticEngine.cpp</code>. That function switches on the type of argument. Since I knew I had a <code>Type</code>, I found the relevant case:</p>

<div class="codehilite"><pre><span></span><code>  <span class="k">case</span> <span class="n">DiagnosticArgumentKind</span><span class="o">::</span><span class="nl">Type</span><span class="p">:</span> <span class="p">{</span>
    <span class="n">assert</span><span class="p">(</span><span class="n">Modifier</span><span class="p">.</span><span class="n">empty</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="s">&quot;Improper modifier for Type argument&quot;</span><span class="p">);</span>

    <span class="c1">// Strip extraneous parentheses; they add no value.</span>
    <span class="k">auto</span> <span class="n">type</span> <span class="o">=</span> <span class="n">Arg</span><span class="p">.</span><span class="n">getAsType</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">getWithoutParens</span><span class="p">();</span>
    <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">typeName</span> <span class="o">=</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">getString</span><span class="p">();</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">shouldShowAKA</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">typeName</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">llvm</span><span class="o">::</span><span class="n">SmallString</span><span class="o">&lt;</span><span class="mi">256</span><span class="o">&gt;</span> <span class="n">AkaText</span><span class="p">;</span>
      <span class="n">llvm</span><span class="o">::</span><span class="n">raw_svector_ostream</span> <span class="n">OutAka</span><span class="p">(</span><span class="n">AkaText</span><span class="p">);</span>
      <span class="n">OutAka</span> <span class="o">&lt;&lt;</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">getCanonicalType</span><span class="p">();</span>
      <span class="n">Out</span> <span class="o">&lt;&lt;</span> <span class="n">llvm</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="n">FormatOpts</span><span class="p">.</span><span class="n">AKAFormatString</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">typeName</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span>
                          <span class="n">AkaText</span><span class="p">.</span><span class="n">c_str</span><span class="p">());</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">Out</span> <span class="o">&lt;&lt;</span> <span class="n">FormatOpts</span><span class="p">.</span><span class="n">OpeningQuotationMark</span> <span class="o">&lt;&lt;</span> <span class="n">typeName</span>
          <span class="o">&lt;&lt;</span> <span class="n">FormatOpts</span><span class="p">.</span><span class="n">ClosingQuotationMark</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">break</span><span class="p">;</span>
  <span class="p">}</span>
</code></pre></div>

<p><code>type-&gt;getString()</code> stood out. Jumping to its definition wasn’t very enlightening:</p>

<div class="codehilite"><pre><span></span><code><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">TypeBase</span><span class="o">::</span><span class="n">getString</span><span class="p">(</span><span class="k">const</span> <span class="n">PrintOptions</span> <span class="o">&amp;</span><span class="n">PO</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
  <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">Result</span><span class="p">;</span>
  <span class="n">llvm</span><span class="o">::</span><span class="n">raw_string_ostream</span> <span class="n">OS</span><span class="p">(</span><span class="n">Result</span><span class="p">);</span>
  <span class="n">print</span><span class="p">(</span><span class="n">OS</span><span class="p">,</span> <span class="n">PO</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">OS</span><span class="p">.</span><span class="n">str</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div>

<p>But jumping through a couple <code>print</code> functions like this eventually led me to this function:</p>

<div class="codehilite"><pre><span></span><code><span class="kt">void</span> <span class="n">Type</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">ASTPrinter</span> <span class="o">&amp;</span><span class="n">Printer</span><span class="p">,</span> <span class="k">const</span> <span class="n">PrintOptions</span> <span class="o">&amp;</span><span class="n">PO</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">isNull</span><span class="p">())</span>
    <span class="n">Printer</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;&lt;null&gt;&quot;</span><span class="p">;</span>
  <span class="k">else</span>
    <span class="nf">TypePrinter</span><span class="p">(</span><span class="n">Printer</span><span class="p">,</span> <span class="n">PO</span><span class="p">).</span><span class="n">visit</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>

<p><code>TypePrinter</code> definitely looked like it was what I needed. It had a series of <code>visit*</code> methods for different types. I knew I had a <code>DependentMemberType</code>, since I’d constructed it above, so I looked for that method.</p>

<div class="codehilite"><pre><span></span><code>  <span class="kt">void</span> <span class="nf">visitDependentMemberType</span><span class="p">(</span><span class="n">DependentMemberType</span> <span class="o">*</span><span class="n">T</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">visitParentType</span><span class="p">(</span><span class="n">T</span><span class="o">-&gt;</span><span class="n">getBase</span><span class="p">());</span>
    <span class="n">Printer</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;.&quot;</span><span class="p">;</span>
    <span class="n">Printer</span><span class="p">.</span><span class="n">printName</span><span class="p">(</span><span class="n">T</span><span class="o">-&gt;</span><span class="n">getName</span><span class="p">());</span>
  <span class="p">}</span>
</code></pre></div>

<p>The parent type was clearly what I was after.</p>

<div class="codehilite"><pre><span></span><code>  <span class="kt">void</span> <span class="nf">visitParentType</span><span class="p">(</span><span class="n">Type</span> <span class="n">T</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">PrintOptions</span> <span class="n">innerOptions</span> <span class="o">=</span> <span class="n">Options</span><span class="p">;</span>
    <span class="n">innerOptions</span><span class="p">.</span><span class="n">SynthesizeSugarOnTypes</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">sugarType</span> <span class="o">=</span> <span class="n">dyn_cast</span><span class="o">&lt;</span><span class="n">SyntaxSugarType</span><span class="o">&gt;</span><span class="p">(</span><span class="n">T</span><span class="p">.</span><span class="n">getPointer</span><span class="p">()))</span>
      <span class="n">T</span> <span class="o">=</span> <span class="n">sugarType</span><span class="o">-&gt;</span><span class="n">getImplementationType</span><span class="p">();</span>

    <span class="n">TypePrinter</span><span class="p">(</span><span class="n">Printer</span><span class="p">,</span> <span class="n">innerOptions</span><span class="p">).</span><span class="n">visit</span><span class="p">(</span><span class="n">T</span><span class="p">);</span>
  <span class="p">}</span>
</code></pre></div>

<p><code>SynthesizeSugarOnTypes</code> definitely seemed like it would control this. <code>[Int]</code> is syntactic sugar for <code>Array&lt;Int&gt;</code>. Just to be sure, I set a debugger breakpoint after that line and ran <code>e innerOptions.SynthesizeSugarOnTypes = true</code> in the debugger. After continuing, I saw <code>[Int].SubSequence</code> just as I was hoping.</p>

<p>But since this was explicitly turned off, I decided to leave it for now. It seemed like a much larger and potentially unwanted change. So this ended up being an insightful, but otherwise unproductive, detour.</p>

<h2>Finishing up</h2>

<p>Since I wasn’t sure I could correctly find all the test cases that I’d need to change up front, I decided to let the computer do this for me. I ran <code>./utils/build-script --debug --test</code> while I wrote most of this up. 😄</p>

<p>After the tests finished, I looked at all the failed tests. Once I’d made sure that the failure actually had the error that I’d want, I updated the test expectation to match.</p>

<p>You can see <a href="https://github.com/apple/swift/pull/17210">my PR here</a>.</p>

    ]]></content>
  </entry>

  <entry>
    <title>Entry 1: A better “no subscripts” error</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2018-06-14T13:51:18Z</published>
    <updated>2018-06-14T13:51:18Z</updated>
    <content type='html'><![CDATA[
    <p>While working on <a href="https://bugs.swift.org/browse/SR-7380">SR-7380</a>, I came across this error in Swift 4.1:</p>

<pre><code>error: type 'Int' has no subscript members
_ = 1[foo: 2]
    ^
</code></pre>

<p>It makes sense now, but I was a little confused at the time. (And it was <em>more</em> confusing since it was in a case where it <em>should</em> have been valid except for the bug I was trying to fix.)</p>

<p>But this seemed like something I could improve, so I thought I’d try. (And improving diagnostics is one of the reasons why I’m interested in the type checker.)</p>

<h2>Where does this error come from?</h2>

<p>I actually like debugging from diagnostics (a term that includes errors, warnings, and “notes”) because they’re easy to pin down. In this case, I just had to search for “has no subscript members”.</p>

<p>(As an aside: I finding searching to be one of the best ways to navigate a codebase. I use Xcode’s “Find in Project” and a tool like <a href="https://github.com/ggreer/the_silver_searcher/">the Silver Searcher</a> <em>constantly</em>. A search tool and some basic regexes can get you really far.)</p>

<p>Searching for that error turns up a rather large file, <code>DiagnosticsSema.def</code>. This file is full of definitions like this:</p>

<pre><code>ERROR(type_not_subscriptable,none,
      "type %0 has no subscript members"
      (Type))
</code></pre>

<p>This defines the diagnostic. This one is an error. It’s referred to from the code as <code>type_not_subscriptable</code>. And it has a format string that defines the error and takes a <code>Type</code> as its only argument.</p>

<p>Simple enough.</p>

<h2>What should the error be?</h2>

<p>Deciding what to replace it with was actually the hardest part.</p>

<p>Looking around the definitions file a bit, I eventually noticed errors like this:</p>

<pre><code>error: value of type 'Int' has no member 'foo'
_ = 1.foo()
    ^ ~~~
</code></pre>

<p>I don’t think I’ve ever been confused by this error, so I decided to try to get closer to it.</p>

<p>First, I noticed that it said <em>value of type</em> instead of <em>type</em>. That seemed to make things a lot clearer.</p>

<p>Then I thought that <em>subscript members</em> seemed a little weird. I searched the Swift subscript documentation and never saw anything refer to them as <em>members</em>. (Although I’ve seen that in the code of the compiler.) <em>Member</em>, I think, is generally used in Swift because it encompasses both methods and properties. (And you can’t tell whether <code>foo.bar()</code> is a method or a property from looking at its usage.)</p>

<p>Instead, most of the diagnostics and documentation referred to them as just <em>subscripts</em>. That’s what I’d call them too, so that seemed like a better description.</p>

<pre><code>error: value of type 'Int' has no subscripts
_ = 1[foo: 2]
    ^
</code></pre>

<p>That seemed hugely better to me.</p>

<h2>Finishing Up</h2>

<p>After getting the description in order, I noticed that the name was also a little different than the names of the similar “has no member” errors. So I renamed <code>type_not_subscriptable</code> to <code>could_not_find_value_subscript</code> to match. This was an easy search-and-replace.</p>

<p>The last step was to update the tests. The generated Xcode project doesn’t include the tests files, so I searched from the command line using <a href="https://github.com/ggreer/the_silver_searcher/">the Silver Searcher</a>.</p>

<p>Diagnostic tests look something like this:</p>

<pre><code>func almostSubscriptableValueMismatch(_ as1: AlmostSubscriptable, a: A) {
  as1[a] // expected-error{{type 'AlmostSubscriptable' has no subscript members}}
}
</code></pre>

<p>A comment describes what the expected error or warning is. Updating them is straightforward: you just change the text.</p>

<p>Once I had that, I ran the tests locally to make sure I didn’t miss anything. You can see <a href="https://github.com/apple/swift/pull/17176">my PR here.</a></p>

<p>Updating an existing diagnostic like this is surprisingly easy. Hopefully you’ll keep that in mind next time you’re confused by a Swift error. This is a great way to start contributing to Swift and an easy way that the community could greatly improve the daily lives of Swift developers. (Although it’s often the case that a confusing error is just a bad diagnostic, which is different altogether.)</p>

<p>Next, I hope to add more and better detail to a diagnostic. That’s a bit more involved than this, but combines what I learned here and in <a href="/swift/0">Entry 0</a>.</p>

    ]]></content>
  </entry>

  <entry>
    <title>Entry 0: SR-7380, Ambiguous KeyPath</title>
    <link rel='alternate' type='text/html' href="" />
    <id></id>
    <published>2018-06-13T18:17:34Z</published>
    <updated>2018-06-13T18:17:34Z</updated>
    <content type='html'><![CDATA[
    <p><a href="https://github.com/apple/swift/pull/17094">My first bug fix</a>! Yay!</p>

<p>Here’s the bug I fixed (with a lot of help):</p>

<pre><code>7380.swift:1:16: error: type of expression is ambiguous without more context
"str"[keyPath: \.count]
               ^~~~~~~
</code></pre>

<p>That seems obviously broken. The value is a <code>String</code> literal. They <code>KeyPath</code> should obviously be <code>String.count</code>. So why doesn’t this work?</p>

<h2>How do I debug this thing?</h2>

<p>My first step was to figure out exactly how to debug the issue.</p>

<p>I started by reproducing the issue in the Xcode debugger. Since I’m so familiar with Xcode, this is where I wanted to start. Luckily, Swift supports building with Xcode: you just need to pass <code>--xcode</code> to the build script.</p>

<p>Having done that, I:</p>

<ol>
<li>Opened the generated Xcode project file from inside the build directory</li>
<li>Manually created a scheme for the <code>swift</code> product</li>
<li>Edited the scheme to:
<ol>
<li>Change the Run &gt; Options &gt; Working Directory to the directory where I’m keeping my buggy <code>.swift</code> files.</li>
<li>Edit the Run &gt; Arguments to pass the file I’m trying to compile. I like to keep things simple by reusing the bug number. So I’m working with <code>7380.swift</code> here.</li>
</ol></li>
<li>Ran the code and saw the output in the debugger.</li>
</ol>

<p>Now I could set breakpoints in Xcode and hit them in LLDB.</p>

<p>I had to poke around a bit to set my first breakpoint. I knew that I'd likely be dealing with the type checker, and I knew that type checking was done in <code>swiftSema</code>, the library used for Swift’s <em>semantic analysis</em>.</p>

<p>I eventually stumbled on <code>ConstraintSystem.solve</code>, which seemed like a good place to start.</p>

<p>But I also had to figure out how to print anything meaningful to the debug console. I’ve been pretty spoiled in Objective-C and Swift, where you can override <code>description</code> and use <code>po</code> in the debugger to print out helpful descriptions of your objects.</p>

<p>In C++, I knew that you can use <code>p</code> to print out the address of an object and <code>p *object</code> to print out its memory contents, but that’s not a super helpful description.</p>

<p>While visiting a WWDC lab, I discovered that, within the constraint system at least, most objects have a <code>dump()</code> method you can call <code>e object.dump()</code> to print out a friendly description. <em>Now</em> I could debug.</p>

<h2>Extra Logging</h2>

<p>Swift includes <a href="https://github.com/apple/swift/blob/master/docs/DebuggingTheCompiler.rst">documentation about debugging the compiler</a>. It includes some helpful flags you can pass (inside the scheme’s Run &gt; Arguments or on the command-line) to get some helpful diagnostics.</p>

<p>Since I knew that I’d be debugging the type-checker, I added <code>-Xfrontend -debug-constraints</code>. (<code>swiftc</code> is the actual compiler, which takes the <code>-debug-constraints</code> flag; <code>-Xfrontend</code> tells <code>swift</code> to pass the next flag through to <code>swiftc</code>.)</p>

<p>That yields a lot of helpful output that shows the type-checker’s search for a solution. But—as I found out later—it doesn’t show you <em>everything</em>. Stepping through with the debugger and dumping this is still very helpful.</p>

<p>Dumping the AST (Abstract Syntax Tree) is also very helpful.</p>

<h2>What is type checking?</h2>

<p>When you write a Swift program, the compiler puts it through a number of stages that transform some input into some output.</p>

<p>To start with, you have just a file. That’s what we typically work with day-to-day. It’s effectively a big string. The lexer lexes the input stream into a stream of tokens: it transforms <code>if foo { blah() }</code> into something like <code>[Token](.if, .identifier(“foo”), .openBrace, .identifier(“blah”), .leftParen, .rightParen, .closeBrace)</code>.</p>

<p>The parser transforms a stream of tokens into an <em>abstract syntax tree</em> (AST). This adds semantics to the tokens, creating something with meaning. That gives something like <code>[Expression](.if(.variable(“foo”), call("blah", []), nil)</code>.</p>

<p>But not all valid syntactically-valid Swift programs are semantically valid. e.g. <code>””[keyPath: \Int.description]</code> is valid syntax, but has invalid semantics. In this example, the types don’t line up: <code>""</code> is a <code>String</code>, but the <code>KeyPath</code> is on <code>Int</code>.</p>

<p>The type-checker (part of the <em>Sema</em> or <em>Semantic Analysis</em> library in Swift) takes an AST and (1) infers missing types and (2) validates all the types.</p>

<h2>How does type checking work?</h2>

<p>Type-checking uses a constraint solver to solve a system of unknowns. I’ve <a href="http://matt.diephouse.com/2016/12/logic-programming-in-swift/">written</a> about how constraint solvers work generally and open sourced <a href="https://github.com/mdiep/Logician">Logician</a>, a Swift constraint solver library. Those are probably worth checking out if this sounds interesting.</p>

<p>In case of the Swift type-checker, a number of <em>type variables</em> are created for unknown types. Then constraints are added between the types and type variables. Best guesses are made. Swift takes valid solutions, scores them, and chooses one. There’s a lot more detail in <a href="https://github.com/apple/swift/blob/master/docs/TypeChecker.rst">the Swift documentation</a>.</p>

<p>Code-wise, type-checking is done expression-by-expression. The constraint system walks the AST with an <code>ASTWalker</code>, generating constraints for each node in the AST of that expression. Once the constraints are generated, the problem can be solved.</p>

<p>Solving works by simplifying constraints and trying out different possible values. If a string literal is used without an explicit type, e.g., then it’ll be constrained to types that are <code>ExpressibleByStringLiteral</code>. The type-checker might start by checking whether all the constraints are met with an actual <code>String</code>.</p>

<p>Once the expression has been type-checked, the AST is rewritten to add the type annotations.</p>

<p>The interesting bits are in a few files:</p>

<ul>
<li><code>CSGen.cpp</code>: Generates the constraints from the AST</li>
<li><code>CSSolver.cpp</code>: Does the actual solving</li>
<li><code>CSSimplify.cpp</code>: Breaks down constraints once more information is known</li>
<li><code>CSDiag.cpp</code>: Diagnoses type-checking failure, producing warnings and errors</li>
<li><code>CSApply.cpp</code>: Rewrites the AST by applying the solution</li>
<li><code>ConstraintSystem.cpp</code>: The core object that tracks constraints and assignments</li>
</ul>

<p>If you dump the <code>ConstraintSystem</code>, you’ll see something like this:</p>

<pre><code>Score: 0 0 0 0 0 0 0 0 0 0 0
Type Variables:
  $T0 as String @ locator@0x119800400 [StringLiteral@7380.swift:1:1]
  $T1 subtype_of_existential bindings={} @ locator@0x119800490 [KeyPath@7380.swift:1:16]
  $T2 [lvalue allowed] fully_bound subtype_of_existential involves_type_vars bindings={} @ locator@0x119800490 [KeyPath@7380.swift:1:16]
  $T3 subtype_of_existential involves_type_vars bindings={} @ locator@0x119800490 [KeyPath@7380.swift:1:16]
  $T4 subtype_of_existential involves_type_vars bindings={} @ locator@0x119800490 [KeyPath@7380.swift:1:16]
  $T5 subtype_of_existential involves_type_vars bindings={} @ locator@0x1198006c0 [Subscript@7380.swift:1:6 -&gt; subscript index]
  $T6 [lvalue allowed] subtype_of_existential bindings={} @ locator@0x1198006e0 [Subscript@7380.swift:1:6 -&gt; subscript result]
  $T7 subtype_of_existential bindings={} @ locator@0x119800cf8 [Subscript@7380.swift:1:6 -&gt; subscript member -&gt; function argument]
  $T8 [lvalue allowed] subtype_of_existential involves_type_vars bindings={} @ locator@0x119800cf8 [Subscript@7380.swift:1:6 -&gt; subscript member -&gt; function argument]
  $T9 subtype_of_existential involves_type_vars bindings={} @ locator@0x119800cf8 [Subscript@7380.swift:1:6 -&gt; subscript member -&gt; function argument]

Active Constraints:

Inactive Constraints:
  @lvalue $T1[.count: value] == $T2 [[locator@0x119800510 [KeyPath@7380.swift:1:16 -&gt; key path component #0]]];
  $T2 equal $T3 [[locator@0x119800490 [KeyPath@7380.swift:1:16]]];
  $T4 key path from $T1 -&gt; $T3 [[locator@0x119800490 [KeyPath@7380.swift:1:16]]];
  (keyPath: $T4) arg tuple conv $T5 [[locator@0x1198006c0 [Subscript@7380.swift:1:6 -&gt; subscript index]]];
  $T8 equal $T9 [[locator@0x119800750 [Subscript@7380.swift:1:6 -&gt; subscript member]]];
</code></pre>

<p>Examining this is the best way to debug type checking.</p>

<h2>What caused the bug?</h2>

<p>Back to the bug I wanted to solve:</p>

<pre><code>7380.swift:1:16: error: type of expression is ambiguous without more context
"str"[keyPath: \.count]
               ^~~~~~~
</code></pre>

<p>Since this is clearly <em>not</em> ambiguous, we must know something that the type-checker doesn’t.</p>

<p>The output of <code>-debug-constraints</code> starts with a dump of the AST:</p>

<pre><code>(subscript_expr type='$T6' location=7380.swift:1:6 range=[7380.swift:1:1 - line:1:23] arg_labels=keyPath:
  (string_literal_expr type='$T0' location=7380.swift:1:1 range=[7380.swift:1:1 - line:1:1] encoding=utf8 value="str" builtin_initializer=**NULL** initializer=**NULL**)
  (tuple_expr type='(keyPath: $T4)' location=7380.swift:1:6 range=[7380.swift:1:6 - line:1:23] names=keyPath
    (keypath_expr type='$T4' location=7380.swift:1:16 range=[7380.swift:1:16 - line:1:18]
      (component=unresolved_property count type=&lt;null&gt;)
      &lt;&lt;null&gt;&gt;
      (unresolved_dot_expr type='&lt;null&gt;' field 'count' function_ref=unapplied
        (key_path_dot_expr implicit type='&lt;null&gt;')))))
</code></pre>

<p>There’s a subscript on a string literal that has an arguments tuple with a <code>keyPath:</code> label and a KeyPath expression inside with an unresolved dot expression. (Unresolved meaning that we don’t know what type it’s on.)</p>

<p>Next, <code>-debug-constraints</code> prints the initial <code>ConstraintSystem</code>:</p>

<pre><code>Score: 0 0 0 0 0 0 0 0 0 0 0
Type Variables:
  $T0 literal=3 bindings={(subtypes of) (default from ExpressibleByStringLiteral) String} @ locator@0x7fa6da01aa00 [StringLiteral@7380.swift:1:1]
  $T1 subtype_of_existential bindings={} @ locator@0x7fa6da01aa90 [KeyPath@7380.swift:1:16]
  $T2 [lvalue allowed] fully_bound subtype_of_existential involves_type_vars bindings={} @ locator@0x7fa6da01aa90 [KeyPath@7380.swift:1:16]
  $T3 subtype_of_existential involves_type_vars bindings={} @ locator@0x7fa6da01aa90 [KeyPath@7380.swift:1:16]
  $T4 subtype_of_existential involves_type_vars bindings={} @ locator@0x7fa6da01aa90 [KeyPath@7380.swift:1:16]
  $T5 fully_bound subtype_of_existential involves_type_vars bindings={} @ locator@0x7fa6da01acc0 [Subscript@7380.swift:1:6 -&gt; subscript index]
  $T6 [lvalue allowed] fully_bound subtype_of_existential involves_type_vars bindings={} @ locator@0x7fa6da01ace0 [Subscript@7380.swift:1:6 -&gt; subscript result]

Active Constraints:

Inactive Constraints:
  $T0 literal conforms to ExpressibleByStringLiteral [[locator@0x7fa6da01aa00 [StringLiteral@7380.swift:1:1]]];
  @lvalue $T1[.count: value] == $T2 [[locator@0x7fa6da01ab10 [KeyPath@7380.swift:1:16 -&gt; key path component #0]]];
  $T2 equal $T3 [[locator@0x7fa6da01aa90 [KeyPath@7380.swift:1:16]]];
  $T4 key path from $T1 -&gt; $T3 [[locator@0x7fa6da01aa90 [KeyPath@7380.swift:1:16]]];
  $T0[.subscript: value] == ($T5) -&gt; $T6 [[locator@0x7fa6da01ad50 [Subscript@7380.swift:1:6 -&gt; subscript member]]];
  (keyPath: $T4) arg tuple conv $T5 [[locator@0x7fa6da01acc0 [Subscript@7380.swift:1:6 -&gt; subscript index]]];
</code></pre>

<p>There are a bunch of type variables (unknown types). It’s worth trying to work through them on your own.</p>

<p>From here, we can see why Swift thinks the type of the <code>KeyPath</code> is ambiguous: <code>$T0</code> (the type of the string literal) has no real connection to <code>$T1</code> (the root type of the <code>KeyPath</code>). But note that this is just the initial constraints—additional constraints could be provided later that provide this connection.</p>

<p>At this point, Swift hasn’t determined that the subscript is a key path application—it could be any subscript. Subscripts can be overloaded, so an <code>OverloadChoice</code> will be created to try the different choices. This is a <em>disjunction</em>—a spot where Swift can try different solutions.</p>

<p>The rest of <code>-debug-constraints</code> shows Swift trying a few different subscript overloads and then trying to diagnose the type failure. You can set a breakpoint inside the overload choice, step through, and dump the <code>ConstraintSystem</code> to see its logic.</p>

<h2>What was the fix?</h2>

<p>Well I had quite a bit of help with this in WWDC labs at and try! Swift San Jose. 😅</p>

<p>But it eventually became clear that an additional constraint was needed to tie the type the subscript was operating on to the root type of the <code>KeyPath</code>. But (1) where should that happen and (2) how do you do it?</p>

<p>The right place seems to be inside <code>ConstraintSystem::addKeyPathApplicationConstraint</code>. This is called from within the subscript’s <code>OverloadChoice</code>, when the type checker is trying to solve it as a <code>KeyPath</code> application. That’s the outermost code (excluding the code inside the overload choice) that knows that we’re trying to use a <code>KeyPath</code> application.</p>

<p>That method is called with a <code>ConstraintLocatorBuilder</code>, which can build a <code>ConstraintLocator</code>. A <code>ConstraintLocator</code> locates the origin of a constraint within an expression. The builder, well, can build a locator. In this case, we didn’t need to build an actual locator, we just need to ask it for its parts.</p>

<p>That yields the origin of the key path application constraint, which in this case will be a subscript expression. By looking inside there, and conditionally casting the expressions that we find, we can look for nodes that match the AST we’re interested in.</p>

<p>If this is the case we’re interested in, we can use the <code>KeyPath</code> expression (representing <code>\.count</code>) to find the <code>KeyPath</code> constraint. Once we have that, it’s easy to add a new constraint between the type the subscript operates on and the root type of the <code>KeyPath</code>. With that additional information, Swift can resolve <code>\.count</code> correctly! Bug fixed!</p>

    ]]></content>
  </entry>

</feed>