<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><br><div><div>On Apr 28, 2014, at 2:13 PM, Richard Smith <<a href="mailto:richard@metafoo.co.uk">richard@metafoo.co.uk</a>> wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Sat, Apr 26, 2014 at 8:35 PM, Ben Langmuir <span dir="ltr"><<a href="mailto:blangmuir@apple.com" target="_blank">blangmuir@apple.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">As discussed here <a href="http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20140414/103530.html" target="_blank">http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20140414/103530.html</a><br>
<br>
This patch checks whether the diagnostic options that could lead to errors (principally -Werror) are consistent between when a module was built and when it is loaded. If there are new -Werror flags, then the module is rebuilt. In order to canonicalize the options we do this check at the level of the constructed DiagnosticsEngine, which contains the final set of diag to diagnostic level mappings. Currently we only rebuild with the new diagnostic options, but we intend to refine this in the future to include the union of the new and old flags, since we know the old ones did not cause errors. System modules are only rebuilt when -Wsystem-headers is enabled.<br>
<br>
One oddity is that unlike checking language options, we don’t perform this diagnostic option checking when loading from a precompiled header. The reason for this is that the compiler cannot rebuild the PCH, so anything that requires it to be rebuilt effectively leaks into the build system. And in this case, that would mean the build system understanding the complex relationship between diagnostic options and the underlying diagnostic mappings, which is unreasonable.</blockquote>
<div><br></div><div>More than that, if the AST file was explicitly built by the user, they may *intend* to apply different warning flags to the two cases, and we shouldn't stop them from doing that. (Likewise, we will eventually want a properly-productized way to explicitly build modules, and this constraint shouldn't apply to such explicitly-built modules either.)</div></div></div></div></blockquote><div><br></div>Makes sense to me.</div><div><br><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">
<div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Skipping the check is safe, because these options do not affect the generated AST. You simply won’t get new build errors due to changed -Werror options automatically, which is also true for non-module cases.</blockquote>
<div><br></div><div><div>@@ -4481,18 +4579,18 @@ bool ASTReader::ParseTargetOptions(const RecordData &Record,</div><div> </div><div> bool ASTReader::ParseDiagnosticOptions(const RecordData &Record, bool Complain,</div>
<div> ASTReaderListener &Listener) {</div><div>- DiagnosticOptions DiagOpts;</div><div>+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions);</div><div>
unsigned Idx = 0;</div><div>-#define DIAGOPT(Name, Bits, Default) DiagOpts.Name = Record[Idx++];</div><div>+#define DIAGOPT(Name, Bits, Default) DiagOpts->Name = Record[Idx++];</div><div> #define ENUM_DIAGOPT(Name, Type, Bits, Default) \</div>
<div>- DiagOpts.set##Name(static_cast<Type>(Record[Idx++]));</div><div>+ DiagOpts->set##Name(static_cast<Type>(Record[Idx++]));</div><div> #include "clang/Basic/DiagnosticOptions.def"</div><div>
</div><div> for (unsigned N = Record[Idx++]; N; --N) {</div><div>- DiagOpts.Warnings.push_back(ReadString(Record, Idx));</div><div>+ DiagOpts->Warnings.push_back(ReadString(Record, Idx));</div><div> }</div><div>
</div><div>- return Listener.ReadDiagnosticOptions(DiagOpts, Complain);</div><div>+ return Listener.ReadDiagnosticOptions(*DiagOpts, Complain);</div><div> }</div><div> </div><div> bool ASTReader::ParseFileSystemOptions(const RecordData &Record, bool Complain,</div>
</div><div><br></div><div><br></div><div>This change suggests that the interface to ReadDiagnosticOptions has become error-prone: if we now need the DiagnosticOptions object to outlive the call to ReadDiagnosticOptions, it should not be passed by const reference. If we need it to be allocated inside an IntrusiveRefCntPtr, we should require that in the interface. What's going on here?</div></div></div></div></blockquote><div><br></div><div>The latter. DiagnosticsEngine expects DiagOpts to be an IntrusiveRefCntPtr. I will change the interfaces to use the correct types.</div><div><br></div><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">
<div><br></div><div><br></div><div><div>+ if ((*ModuleMgr.begin())->Kind != serialization::MK_Module)</div><div>+ return false;</div></div><div><br></div><div>This doesn't look quite right. We could be loading a new module file that's unrelated to the PCH or preamble, and in that case we should apply these checks.</div></div></div></div></blockquote><div><br></div><div>True. I suppose iterating ImportedBy[0] is good enough to tell us whether this came from an MK_Module or not.</div><div><br></div><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">
<div><br></div><div><br></div><div><div>+ // Check current mappings for new -Werror mappings</div><div>+ for (auto DiagIDMappingPair : Diags.getDiagnosticMappings()) {</div><div>+ diag::kind DiagID = DiagIDMappingPair.first;</div>
<div>+ Level CurLevel = Diags.getDiagnosticLevel(DiagID, SourceLocation());</div><div>+ if (CurLevel <= DiagnosticsEngine::Warning)</div><div>+ continue; // not significant</div><div>+ Level StoredLevel = StoredDiags.getDiagnosticLevel(DiagID, SourceLocation());</div>
<div>+ if (StoredLevel <= DiagnosticsEngine::Warning) {</div><div>+ if (Complain)</div><div>+ Diags.Report(diag::err_pch_diagopt_mismatch) << "-Werror=" +</div><div>+ Diags.getDiagnosticIDs()->getWarningOptionForDiag(DiagID).str();</div>
<div>+ return true;</div><div>+ }</div><div>+ }</div><div>+</div><div>+ // Check the stored mappings for cases that were explicitly mapped to *not* be</div><div>+ // errors that are now errors because of options like -Werror.</div>
<div>+ for (auto DiagIDMappingPair : StoredDiags.getDiagnosticMappings()) {</div><div>+ diag::kind DiagID = DiagIDMappingPair.first;</div><div>+ Level StoredLevel = StoredDiags.getDiagnosticLevel(DiagID, SourceLocation());</div>
<div>+ if (StoredLevel >= DiagnosticsEngine::Error)</div><div>+ continue; // already an error</div><div>+ Level CurLevel = Diags.getDiagnosticLevel(DiagID, SourceLocation());</div><div>+ if (CurLevel >= DiagnosticsEngine::Error) {</div>
<div>+ if (Complain)</div><div>+ Diags.Report(diag::err_pch_diagopt_mismatch) << "-Werror=" +</div><div>+ Diags.getDiagnosticIDs()->getWarningOptionForDiag(DiagID).str();</div><div>
+ return true;</div><div>+ }</div><div>+ }</div></div><div><br></div><div>Your two loop bodies are semantically identical; can you factor out the redundancy here? (I guess you're duplicating this to try to avoid adding extra diagnostic mapping entries?)</div>
<div><br></div></div></div></div></blockquote>Sure, I can simplify this.</div><div><br><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div><div>+static bool checkDiagnosticMappings(DiagnosticsEngine &StoredDiags,</div><div>+ DiagnosticsEngine &Diags,</div><div>+ bool IsSystem, bool Complain) {</div>
</div><div><br></div><div>We should also check for differences in ExtBehavior here (in case the user has enabled -fpedantic-errors).</div></div></div></div>
</blockquote></div><br><div>Yes we should!</div></body></html>