<div dir="ltr"><div dir="ltr"><div>Thanks all for the feedback :)</div><div>Actually, I think there is two different things here:</div><div>1) Improving checks on pure virtual functions. <br></div><div>2) Adding checks on non-pure virtual functions (my initial suggestion)<br></div><div><br></div><div>I agree that it seems sensible to have 1) being done as part of a static analyzer, since as you said, you don't expect the compiler to perform (costly) in-depth analysis of the code.<br></div><div>FWIW note that with today's warning (<span class="gmail-blob-code-inner gmail-blob-code-marker-addition">call-to-pure-virtual-from-ctor-dtor)</span>, a case like D, but with foo's function content directly written in D's constructor will raise a warning. i.e. there is no data flow analysis.</div><div>In this case, the correct warning IMO should be to tell the user this part of the code will never be executed (i.e. unreachable-code, which doesn't catch it, probably for the same reason that it doesn't perform data flow analysis)</div><div><br></div><div>Going back to 2) my suggestion was about cases like B.</div><div>"diagnosable, but possibly correct": indeed, but IMHO this is the point of warnings: to point you to things that are correct but likely to be an error.</div><div>I think here the important thing is what ratio "likely" corresponds to, as you said.<br></div><div><br></div><div>What is usually the acceptable ratio for new warnings?</div><div><br></div><div>In the case of B, IMO the correct code should be:</div><div><div>struct B {</div><div>        B() { B::do_foo(); }<br></div><div>        virtual void do_foo();</div><div>};</div></div><div>Which does the same thing, but is clearer for anyone reading the code, since it removes any ambiguity.</div><div><br></div><div>Agreed this could be useful as part of static analyzing as well, but having some lightweight checks as part of the compilation process is also useful if it catches most obvious cases: as I said, this can save developers time if they notice it right away, instead of having to wait for their changes to go through CI/static code analysis step (the sooner you catch potential errors, the better it is).<br></div><div><br></div><div>Arnaud</div><div><br></div></div></div><div dir="ltr"><div><br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr">Le ven. 14 déc. 2018 à 02:19, Artem Dergachev <<a href="mailto:noqnoqneo@gmail.com" target="_blank">noqnoqneo@gmail.com</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
  
    
  
  <div bgcolor="#FFFFFF">
    Yup, Static Analyzer has a checker for this, and its current status
    is "opt-in" (i.e., the checker is `optin.cplusplus.VirtualCall`). So
    we it's stable and more or less full-featured and we encourage you
    to try it out, but it's off by default because it finds non-bugs as
    well (like the case B). We should definitely enable-by-default the
    part of it that finds *pure* virtual calls. I wonder why didn't we
    do that already. There's even an option to enable such behavior.<br>
    <br>
    Yes, indeed, cases like "C" are the reason why this checker was
    made.<br>
    <br>
    And that's also the reason why this check requires something as
    powerful as a full-featured "symbolic execution" to be useful, which
    is something that's too slow to be a compiler warning. The previous
    attempt on this checker was simply scanning the AST for virtual
    function calls and went through the call graph to see if some of
    these virtual calls are reachable from the constructor. However,
    this approach was having false positives when there was no actual
    execution path that would result in going through the call graph in
    that order during construction. Eg.,<br>
    <br>
        struct D {<br>
            bool Flag;<br>
            void setFlag() { Flag = true; } // The flag can be set
    later.<br>
    <br>
            D() : Flag(false) { foo(); } // But its initial value is
    "clear".<br>
            void foo() { if (Flag) bar(); } // Flag is still clear when
    called from D().<br>
            virtual void bar() = 0;<br>
        }<br>
    <br>
    In this case if you look at the AST you'll see that D() calls foo(),
    foo() calls bar(), bar() is pure virtual. But there's no bug in this
    code, because foo() never actually calls bar() when called from the
    constructor. The VirtualCall checker, in its current state, is able
    to understand this sort of stuff as well (up to overall modeling
    bugs in the Static Analyzer).<br>
    <br>
    A warning could still be used to catch some easier patterns, eg.,
    when *all* paths through the constructor from a certain point result
    in a pure virtual function call. Eg., if you simplify the problem
    down to finding the bug when D::foo() is defined as something like
    `if (Flag) bar(); else bar();`, it may be possible to come up with
    an efficient data flow analysis that will catch it and have no false
    positives. But it still needs to be inter-procedural, so it's
    actually still pretty hard and we will still probably have to bail
    out at some stack depth. This would most likely become the most
    sophisticated compiler warning we have in Clang: we have a few data
    flow warnings, such as "variable may be used uninitialized in this
    function", but none of them are inter-procedural.<br>
    <br>
    So, yeah, i believe that coming up with a compiler warning is indeed
    relatively hard () and implementing it as a Static Analyzer warning
    is most likely the right approach.<br>
    <br>
    <br>
    <div class="gmail-m_4759335717445627117gmail-m_428474311560325971gmail-m_6064893394642050137moz-cite-prefix">On 12/13/18 12:24 PM, Arthur O'Dwyer
      via cfe-dev wrote:<br>
    </div>
    <blockquote type="cite">
      
      <div dir="ltr">
        <div dir="ltr">
          <div dir="ltr">On Thu, Dec 13, 2018 at 1:21 PM Arnaud Bienner
            via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>>
            wrote:<br>
            <div class="gmail_quote">
              <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
                <div dir="ltr">
                  <div>What I like about having this kind of diagnostics
                    being part of clang itself is that you can turn them
                    into errors using -Werror=, and prevent bugs to even
                    been written, saving you debugging time.</div>
                  <div>I'm not sure how static code analysis is done on
                    the projects you are working on, but from my
                    experience, it is usually done as part of the CI (so
                    after you commit your changes): this saves you from
                    having bugs reaching the release stage, but doesn't
                    save you the time spent debugging a silly error that
                    could have been catch by the compiler.</div>
                </div>
              </blockquote>
              <div><br>
              </div>
              <div>FWIW, I think most projects run CI on pull requests,
                so, the code has to pass CI before it's allowed to hit
                master. (This is how you keep your master CI green all
                the time, instead of flashing red-green-red-green.)</div>
              <div><br>
              </div>
              <div>I think any diagnostic in this area is really too
                fragile to be useful. Consider:</div>
              <div><br>
              </div>
              <div>    struct A {</div>
              <div>        A() { do_foo(); }  // diagnosable and
                definitely a bug</div>
              <div>        virtual void do_foo() = 0;<br>
              </div>
              <div>    };</div>
              <div>
                <div>    struct B {</div>
                <div>        B() { do_foo(); }  // diagnosable, but
                  possibly correct</div>
                <div>        virtual void do_foo();</div>
                <div>    };</div>
              </div>
              <div>    struct C {</div>
              <div>        C() { foo(); }  // definitely a bug, but not
                diagnosable</div>
              <div>        void foo() { do_foo(); }  // best practice</div>
              <div>    private:</div>
              <div>        virtual void do_foo() = 0;</div>
              <div>    };</div>
              <div><br>
              </div>
              <div>Clang, GCC, and ICC all diagnose "A". Nobody
                diagnoses "B". Nobody diagnoses "C", even though it's
                exactly isomorphic to "A" — and "C" (splitting
                customization points into a public non-virtual interface
                and a private virtual implementation) is what we want to
                teach people to do!</div>
              <div><br>
              </div>
              <div>I think Clang can't <i>remove</i> its diagnostic for
                "A" because that would be a regression versus GCC and
                ICC; but I don't think it's worth spending time teaching
                Clang to catch "B" (with its attendant false positives)
                given that "C" is the most interesting case in practice,
                and Clang will never be able to catch "C".</div>
              <div><br>
              </div>
              <div>The static analyzer, though, could definitely catch
                "C"! I don't know if it does today or not.</div>
              <div><br>
              </div>
              <div>my $.02,</div>
              <div>–Arthur</div>
            </div>
          </div>
        </div>
      </div>
      <br>
      <fieldset class="gmail-m_4759335717445627117gmail-m_428474311560325971gmail-m_6064893394642050137mimeAttachmentHeader"></fieldset>
      <pre class="gmail-m_4759335717445627117gmail-m_428474311560325971gmail-m_6064893394642050137moz-quote-pre">_______________________________________________
cfe-dev mailing list
<a class="gmail-m_4759335717445627117gmail-m_428474311560325971gmail-m_6064893394642050137moz-txt-link-abbreviated" href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>
<a class="gmail-m_4759335717445627117gmail-m_428474311560325971gmail-m_6064893394642050137moz-txt-link-freetext" href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev</a>
</pre>
    </blockquote>
    <br>
  </div>

</blockquote></div>