This is an archive of the discontinued LLVM Phabricator instance.

Reapply "Fix crash on switch conditions of non-integer types in templates"
ClosedPublic

Authored by eandrews on Nov 7 2019, 8:12 AM.

Details

Summary

This patch reapplies D61027. When D61027 was previously committed (76945821b9cad3), buildbots failed due to clang-tidy test fails. The test fails are because some errors in templates are now diagnosed earlier (does not wait till instantiation). I have modified the tests to add checks for these diagnostics/prevent these diagnostics.

Since I have not worked on clang-tidy in the past, I am hoping someone with more familiarity in this area can take a look and review my changes. There are no code changes in this second attempt (compared to D61027). I have only modified failing tests.

Summary of code changes (pasted from D61027) is below -

Clang currently crashes for switch statements inside a template when the condition is a non-integer field member because contextual implicit conversion is skipped when parsing the condition. This conversion is however later checked in an assert when the case statement is handled. The conversion is skipped when parsing the condition because the field member is set as type-dependent based on its containing class. This patch sets the type dependency based on the field's type instead. This patch fixes Bug 40982.

Diff Detail

Event Timeline

eandrews created this revision.Nov 7 2019, 8:12 AM
rnk accepted this revision.Nov 7 2019, 2:02 PM

I don't have experience with clang-tidy tests, but I think these two small, test-only changes are small enough that you can go ahead and commit them without review from a clang-tidy owner. lgtm

This revision is now accepted and ready to land.Nov 7 2019, 2:02 PM
gribozavr2 accepted this revision.Nov 7 2019, 4:10 PM

LGTM for ClangTidy, assuming you ran ninja check-all.

Thank you for taking a look Reid and Dmitri. There were no fails with check-all.

My commit rights have not been transferred from SVN to Github yet, so Melanie (@mibintc) has kindly agreed to commit this patch for me.

This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptNov 8 2019, 10:17 AM
eandrews added a comment.EditedNov 8 2019, 11:17 AM

@rnk @gribozavr2 clang-tidy test "bugprone-string-integer-assignment.cpp" caused a bot fail on windows. From the logs it looks like the 'CHECK-FIXES' I added in that test is failing because the fix isn't applied. I am not sure why. I don't have a windows build set up and I assume a new build is going to take some time. I am not sure what the protocol for this is. Should I go ahead and revert my patch while I investigate the issue on Windows or should I just push a new patch deleting the 'CHECK-FIXES' while I look into the issue?

Should I go ahead and revert my patch while I investigate the issue on Windows or should I just push a new patch deleting the 'CHECK-FIXES' while I look into the issue?

Yes. As a general rule, if you landed a patch and it broke a bot that was previously green, please revert ASAP and investigate later.

Thank you for letting me know. The patch has been reverted.

The fail is a result of different diagnostics thrown on Windows and Linux. It looks like clang passes -fdelayed-template-parsing by default on Windows, which I assume is the reason why the diagnostic is not generated on Windows. I'm not entirely sure how different diagnostics in various targets are handled by clang-tidy. I'll push a new patch once I take a closer look.

A number of ClangTidy tests have -fno-delayed-template-parsing in their RUN lines. While not a great solution (it only fixes the test, but does not make the warnings show for users on Windows), there's only so much a checker can do to counteract that. Before adding this flag to the affected test, it might be interesting to understand though why this patch had such a consequence though.

eandrews added a comment.EditedNov 11 2019, 12:52 PM

The patch changes type dependency of field 'std::string s' and sets it based on field type (i.e. not type-dependent).

I do not know how clang-tidy works internally, but I assume checker used to skip the field earlier since it was set as dependent (based on containing class 'S'), hence not throwing the warning on any platform? A diagnostic is thrown with this patch possibly because of change in field s' type-dependency.

hence not throwing the warning on any platform?

I'm not sure I understand? The way I read the buildbot breakage, an existing ClangTidy test passed before and after this change, but broke on Windows. The breakage was that the warnings stopped being produced. Usually that is related to delayed parsing of templates on Windows.

hence not throwing the warning on any platform?

The way I read the buildbot breakage, an existing ClangTidy test passed before and after this change, but broke on Windows. The breakage was that the warnings stopped being produced.

The existing test does not have this warning. I modified the test to add the check for this warning since it is generated on Linux after my patch. It is not generated on Windows because of delayed template parsing.

hence not throwing the warning on any platform?

The way I read the buildbot breakage, an existing ClangTidy test passed before and after this change, but broke on Windows. The breakage was that the warnings stopped being produced.

The existing test does not have this warning. I modified the test to add the check for this warning since it is generated on Linux after my patch. It is not generated on Windows because of delayed template parsing.

Is it just that warning that is not generated, or all warnings in that test file?

Anyhow, adding -fno-delayed-template-parsing to a ClangTidy test is not unreasonable, and is indeed existing practice.

hence not throwing the warning on any platform?

The way I read the buildbot breakage, an existing ClangTidy test passed before and after this change, but broke on Windows. The breakage was that the warnings stopped being produced.

The existing test does not have this warning. I modified the test to add the check for this warning since it is generated on Linux after my patch. It is not generated on Windows because of delayed template parsing.

Is it just that warning that is not generated, or all warnings in that test file?

I am pretty sure it is just this warning but I will verify this again and push a new patch with "-fno-delayed-template-parsing" once I confirm . Thanks for all your help!

This (when reapplied in https://reviews.llvm.org/rG878a24ee244a24c39d1c57e9af2) broke compilation of code that earlier built fine. A reduced example:

namespace glslang {
class TPoolAllocator {
  void operator=(TPoolAllocator);
};
template <class> class a {
  TPoolAllocator *b;
  void c() { allocator = *b; }
  TPoolAllocator allocator;
};
} // namespace glslang

This (when reapplied in https://reviews.llvm.org/rG878a24ee244a24c39d1c57e9af2) broke compilation of code that earlier built fine. A reduced example:

namespace glslang {
class TPoolAllocator {
  void operator=(TPoolAllocator);
};
template <class> class a {
  TPoolAllocator *b;
  void c() { allocator = *b; }
  TPoolAllocator allocator;
};
} // namespace glslang

With this patch, some errors in templates are diagnosed earlier (i.e. does not wait till instantiation). Since 'allocator' and 'b' aren't dependent, I think this is a valid diagnosis. GCC throws an error on this code upon instantiation. https://godbolt.org/z/X9Y-Vy

This (when reapplied in https://reviews.llvm.org/rG878a24ee244a24c39d1c57e9af2) broke compilation of code that earlier built fine. A reduced example:

namespace glslang {
class TPoolAllocator {
  void operator=(TPoolAllocator);
};
template <class> class a {
  TPoolAllocator *b;
  void c() { allocator = *b; }
  TPoolAllocator allocator;
};
} // namespace glslang

With this patch, some errors in templates are diagnosed earlier (i.e. does not wait till instantiation). Since 'allocator' and 'b' aren't dependent, I think this is a valid diagnosis. GCC throws an error on this code upon instantiation. https://godbolt.org/z/X9Y-Vy

The original non-reduced case does build fine with GCC though (I'm fairly sure). I can try to reduce one later that still builds with GCC but fails with current clang.

This (when reapplied in https://reviews.llvm.org/rG878a24ee244a24c39d1c57e9af2) broke compilation of code that earlier built fine. A reduced example:

namespace glslang {
class TPoolAllocator {
  void operator=(TPoolAllocator);
};
template <class> class a {
  TPoolAllocator *b;
  void c() { allocator = *b; }
  TPoolAllocator allocator;
};
} // namespace glslang

With this patch, some errors in templates are diagnosed earlier (i.e. does not wait till instantiation). Since 'allocator' and 'b' aren't dependent, I think this is a valid diagnosis. GCC throws an error on this code upon instantiation. https://godbolt.org/z/X9Y-Vy

The original non-reduced case does build fine with GCC though (I'm fairly sure). I can try to reduce one later that still builds with GCC but fails with current clang.

Yes. The reduced example here builds fine with GCC as well, unless template is instantiated. However I don't think Clang always matches GCC behavior when diagnosing errors in templates (See example from cpplearner here - https://reviews.llvm.org/D61027). Clang diagnoses several errors even if template is not instantiated (unlike GCC). I am not sure what the 'correct' behavior is / whether we should match GCC here. @erichkeane @rnk could you please take a look?

This (when reapplied in https://reviews.llvm.org/rG878a24ee244a24c39d1c57e9af2) broke compilation of code that earlier built fine. A reduced example:

namespace glslang {
class TPoolAllocator {
  void operator=(TPoolAllocator);
};
template <class> class a {
  TPoolAllocator *b;
  void c() { allocator = *b; }
  TPoolAllocator allocator;
};
} // namespace glslang

With this patch, some errors in templates are diagnosed earlier (i.e. does not wait till instantiation). Since 'allocator' and 'b' aren't dependent, I think this is a valid diagnosis. GCC throws an error on this code upon instantiation. https://godbolt.org/z/X9Y-Vy

The original non-reduced case does build fine with GCC though (I'm fairly sure). I can try to reduce one later that still builds with GCC but fails with current clang.

Yes. The reduced example here builds fine with GCC as well, unless template is instantiated. However I don't think Clang always matches GCC behavior when diagnosing errors in templates (See example from cpplearner here - https://reviews.llvm.org/D61027). Clang diagnoses several errors even if template is not instantiated (unlike GCC). I am not sure what the 'correct' behavior is / whether we should match GCC here. @erichkeane @rnk could you please take a look?

I'll have to see the reproducer, however Clang DOES diagnose some template errors earlier than GCC in many situations. A template definition that isn't instantiatible is ill-formed (I think NDR), so if the original reproducer doesn't instantiate the template, then it is likely incorrect code.

rnk added a comment.Dec 5 2019, 11:47 AM

This (when reapplied in https://reviews.llvm.org/rG878a24ee244a24c39d1c57e9af2) broke compilation of code that earlier built fine. A reduced example:

namespace glslang {
class TPoolAllocator {
  void operator=(TPoolAllocator);
};
template <class> class a {
  TPoolAllocator *b;
  void c() { allocator = *b; }
  TPoolAllocator allocator;
};
} // namespace glslang

I fixed this particular code upstream:
https://github.com/KhronosGroup/glslang/pull/2010
I am not enough an expert to be sure, but I suspect this is in the area of "invalid, no diagnostic required", where this code is invalid, but a conforming C++ implementation could either reject or accept it. Now we reject it, and that seems better in the long term, even though it creates a fire drill in the short term. =(

In D69950#1771515, @rnk wrote:

I fixed this particular code upstream:
https://github.com/KhronosGroup/glslang/pull/2010
I am not enough an expert to be sure, but I suspect this is in the area of "invalid, no diagnostic required", where this code is invalid, but a conforming C++ implementation could either reject or accept it. Now we reject it, and that seems better in the long term, even though it creates a fire drill in the short term. =(

Ok, thanks for the input! I'll go cherrypick that fix then.

Similar to the case @mstorsjo mentioned, this also causes the following code to fail now:

struct S {
    unsigned size;
};

template <class T>
struct U {
    S s;
    unsigned size() { return s.size(); }
};

This is obviously invalid, and I personally prefer getting the error early instead of having to wait for the instantiation, but it is different from gcc and MSVC: https://godbolt.org/z/yoDDjS. I think the earlier diagnosis should be okay from a conformance point of view though (but I'm far from being well-versed with that).