Skip to content

style: clang-tidy - #2478

Merged
henryiii merged 8 commits into
pybind:masterfrom
henryiii:style/clangtidy
Sep 15, 2020
Merged

style: clang-tidy#2478
henryiii merged 8 commits into
pybind:masterfrom
henryiii:style/clangtidy

Conversation

@henryiii

@henryiiihenryiii commented Sep 11, 2020

Copy link
Copy Markdown
Collaborator

This adds the ability to run a clang-tidy list of checks in CI, and adds a few checks to get started. Due to the methodology used, only checks that made changes (one commit per check) were added. Only extremely safe checks were added. Eventually, it would be nice to expand further; there are some very useful/powerful clang tidy checks. You can see the groups here. You can do groups of checks, like modernize-* (that's C++11), readability-*, portability-*, bugprone-*, cppcoreguidelines-*, or performance-* (and remove ones that you don't support). You can also disable inline for special cases.

Checks added:

  • Namespaces always have a closing comment
  • override is always used when overriding
  • .empty() is used to check empty containers instead of size comparisons
  • using used instead of typedef
  • = default used for trivial bodies (except where MSVC 2015 requires otherwise)
  • auto used when the typename would be otherwise duplicated on the same line (not everywhere, as that would be worse, not better!)
  • emplace used instead of push back for containers

I've also added instructions for running this locally (where you can perform the fixes automatically).

@henryiii

henryiii commented Sep 11, 2020

Copy link
Copy Markdown
CollaboratorAuthor
FYI, this is the full list of checks in Clang 10: (click to expand)
 abseil-duration-addition
abseil-duration-comparison
abseil-duration-conversion-cast
abseil-duration-division
abseil-duration-factory-float
abseil-duration-factory-scale
abseil-duration-subtraction
abseil-duration-unnecessary-conversion
abseil-faster-strsplit-delimiter
abseil-no-internal-dependencies
abseil-no-namespace
abseil-redundant-strcat-calls
abseil-str-cat-append
abseil-string-find-startswith
abseil-time-comparison
abseil-time-subtraction
abseil-upgrade-duration-conversions
android-cloexec-accept
android-cloexec-accept4
android-cloexec-creat
android-cloexec-dup
android-cloexec-epoll-create
android-cloexec-epoll-create1
android-cloexec-fopen
android-cloexec-inotify-init
android-cloexec-inotify-init1
android-cloexec-memfd-create
android-cloexec-open
android-cloexec-pipe
android-cloexec-pipe2
android-cloexec-socket
android-comparison-in-temp-failure-retry
boost-use-to-string
bugprone-argument-comment
bugprone-assert-side-effect
bugprone-bad-signal-to-kill-thread
bugprone-bool-pointer-implicit-conversion
bugprone-branch-clone
bugprone-copy-constructor-init
bugprone-dangling-handle
bugprone-dynamic-static-initializers
bugprone-exception-escape
bugprone-fold-init-type
bugprone-forward-declaration-namespace
bugprone-forwarding-reference-overload
bugprone-inaccurate-erase
bugprone-incorrect-roundings
bugprone-infinite-loop
bugprone-integer-division
bugprone-lambda-function-name
bugprone-macro-parentheses
bugprone-macro-repeated-side-effects
bugprone-misplaced-operator-in-strlen-in-alloc
bugprone-misplaced-widening-cast
bugprone-move-forwarding-reference
bugprone-multiple-statement-macro
bugprone-narrowing-conversions
bugprone-not-null-terminated-result
bugprone-parent-virtual-call
bugprone-posix-return
bugprone-signed-char-misuse
bugprone-sizeof-container
bugprone-sizeof-expression
bugprone-string-constructor
bugprone-string-integer-assignment
bugprone-string-literal-with-embedded-nul
bugprone-suspicious-enum-usage
bugprone-suspicious-memset-usage
bugprone-suspicious-missing-comma
bugprone-suspicious-semicolon
bugprone-suspicious-string-compare
bugprone-swapped-arguments
bugprone-terminating-continue
bugprone-throw-keyword-missing
bugprone-too-small-loop-variable
bugprone-undefined-memory-manipulation
bugprone-undelegated-constructor
bugprone-unhandled-self-assignment
bugprone-unused-raii
bugprone-unused-return-value
bugprone-use-after-move
bugprone-virtual-near-miss
cert-dcl03-c
cert-dcl16-c
cert-dcl21-cpp
cert-dcl50-cpp
cert-dcl54-cpp
cert-dcl58-cpp
cert-dcl59-cpp
cert-env33-c
cert-err09-cpp
cert-err34-c
cert-err52-cpp
cert-err58-cpp
cert-err60-cpp
cert-err61-cpp
cert-fio38-c
cert-flp30-c
cert-mem57-cpp
cert-msc30-c
cert-msc32-c
cert-msc50-cpp
cert-msc51-cpp
cert-oop11-cpp
cert-oop54-cpp
cert-oop58-cpp
cert-pos44-c
clang-analyzer-apiModeling.StdCLibraryFunctions
clang-analyzer-apiModeling.TrustNonnull
clang-analyzer-apiModeling.google.GTest
clang-analyzer-apiModeling.llvm.CastValue
clang-analyzer-apiModeling.llvm.ReturnValue
clang-analyzer-core.CallAndMessage
clang-analyzer-core.DivideZero
clang-analyzer-core.DynamicTypePropagation
clang-analyzer-core.NonNullParamChecker
clang-analyzer-core.NonnilStringConstants
clang-analyzer-core.NullDereference
clang-analyzer-core.StackAddrEscapeBase
clang-analyzer-core.StackAddressEscape
clang-analyzer-core.UndefinedBinaryOperatorResult
clang-analyzer-core.VLASize
clang-analyzer-core.builtin.BuiltinFunctions
clang-analyzer-core.builtin.NoReturnFunctions
clang-analyzer-core.uninitialized.ArraySubscript
clang-analyzer-core.uninitialized.Assign
clang-analyzer-core.uninitialized.Branch
clang-analyzer-core.uninitialized.CapturedBlockVariable
clang-analyzer-core.uninitialized.UndefReturn
clang-analyzer-cplusplus.InnerPointer
clang-analyzer-cplusplus.Move
clang-analyzer-cplusplus.NewDelete
clang-analyzer-cplusplus.NewDeleteLeaks
clang-analyzer-cplusplus.PureVirtualCall
clang-analyzer-cplusplus.SelfAssignment
clang-analyzer-cplusplus.SmartPtr
clang-analyzer-cplusplus.VirtualCallModeling
clang-analyzer-deadcode.DeadStores
clang-analyzer-fuchsia.HandleChecker
clang-analyzer-nullability.NullPassedToNonnull
clang-analyzer-nullability.NullReturnedFromNonnull
clang-analyzer-nullability.NullabilityBase
clang-analyzer-nullability.NullableDereferenced
clang-analyzer-nullability.NullablePassedToNonnull
clang-analyzer-nullability.NullableReturnedFromNonnull
clang-analyzer-optin.cplusplus.UninitializedObject
clang-analyzer-optin.cplusplus.VirtualCall
clang-analyzer-optin.mpi.MPI-Checker
clang-analyzer-optin.osx.OSObjectCStyleCast
clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker
clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker
clang-analyzer-optin.performance.GCDAntipattern
clang-analyzer-optin.performance.Padding
clang-analyzer-optin.portability.UnixAPI
clang-analyzer-osx.API
clang-analyzer-osx.MIG
clang-analyzer-osx.NSOrCFErrorDerefChecker
clang-analyzer-osx.NumberObjectConversion
clang-analyzer-osx.OSObjectRetainCount
clang-analyzer-osx.ObjCProperty
clang-analyzer-osx.SecKeychainAPI
clang-analyzer-osx.cocoa.AtSync
clang-analyzer-osx.cocoa.AutoreleaseWrite
clang-analyzer-osx.cocoa.ClassRelease
clang-analyzer-osx.cocoa.Dealloc
clang-analyzer-osx.cocoa.IncompatibleMethodTypes
clang-analyzer-osx.cocoa.Loops
clang-analyzer-osx.cocoa.MissingSuperCall
clang-analyzer-osx.cocoa.NSAutoreleasePool
clang-analyzer-osx.cocoa.NSError
clang-analyzer-osx.cocoa.NilArg
clang-analyzer-osx.cocoa.NonNilReturnValue
clang-analyzer-osx.cocoa.ObjCGenerics
clang-analyzer-osx.cocoa.RetainCount
clang-analyzer-osx.cocoa.RetainCountBase
clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak
clang-analyzer-osx.cocoa.SelfInit
clang-analyzer-osx.cocoa.SuperDealloc
clang-analyzer-osx.cocoa.UnusedIvars
clang-analyzer-osx.cocoa.VariadicMethodTypes
clang-analyzer-osx.coreFoundation.CFError
clang-analyzer-osx.coreFoundation.CFNumber
clang-analyzer-osx.coreFoundation.CFRetainRelease
clang-analyzer-osx.coreFoundation.containers.OutOfBounds
clang-analyzer-osx.coreFoundation.containers.PointerSizedValues
clang-analyzer-security.FloatLoopCounter
clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling
clang-analyzer-security.insecureAPI.SecuritySyntaxChecker
clang-analyzer-security.insecureAPI.UncheckedReturn
clang-analyzer-security.insecureAPI.bcmp
clang-analyzer-security.insecureAPI.bcopy
clang-analyzer-security.insecureAPI.bzero
clang-analyzer-security.insecureAPI.decodeValueOfObjCType
clang-analyzer-security.insecureAPI.getpw
clang-analyzer-security.insecureAPI.gets
clang-analyzer-security.insecureAPI.mkstemp
clang-analyzer-security.insecureAPI.mktemp
clang-analyzer-security.insecureAPI.rand
clang-analyzer-security.insecureAPI.strcpy
clang-analyzer-security.insecureAPI.vfork
clang-analyzer-unix.API
clang-analyzer-unix.DynamicMemoryModeling
clang-analyzer-unix.Malloc
clang-analyzer-unix.MallocSizeof
clang-analyzer-unix.MismatchedDeallocator
clang-analyzer-unix.Vfork
clang-analyzer-unix.cstring.BadSizeArg
clang-analyzer-unix.cstring.CStringModeling
clang-analyzer-unix.cstring.NullArg
clang-analyzer-valist.CopyToSelf
clang-analyzer-valist.Uninitialized
clang-analyzer-valist.Unterminated
clang-analyzer-valist.ValistBase
cppcoreguidelines-avoid-c-arrays
cppcoreguidelines-avoid-goto
cppcoreguidelines-avoid-magic-numbers
cppcoreguidelines-c-copy-assignment-signature
cppcoreguidelines-explicit-virtual-functions
cppcoreguidelines-init-variables
cppcoreguidelines-interfaces-global-init
cppcoreguidelines-macro-usage
cppcoreguidelines-narrowing-conversions
cppcoreguidelines-no-malloc
cppcoreguidelines-non-private-member-variables-in-classes
cppcoreguidelines-owning-memory
cppcoreguidelines-pro-bounds-array-to-pointer-decay
cppcoreguidelines-pro-bounds-constant-array-index
cppcoreguidelines-pro-bounds-pointer-arithmetic
cppcoreguidelines-pro-type-const-cast
cppcoreguidelines-pro-type-cstyle-cast
cppcoreguidelines-pro-type-member-init
cppcoreguidelines-pro-type-reinterpret-cast
cppcoreguidelines-pro-type-static-cast-downcast
cppcoreguidelines-pro-type-union-access
cppcoreguidelines-pro-type-vararg
cppcoreguidelines-slicing
cppcoreguidelines-special-member-functions
darwin-avoid-spinlock
darwin-dispatch-once-nonstatic
fuchsia-default-arguments-calls
fuchsia-default-arguments-declarations
fuchsia-header-anon-namespaces
fuchsia-multiple-inheritance
fuchsia-overloaded-operator
fuchsia-restrict-system-includes
fuchsia-statically-constructed-objects
fuchsia-trailing-return
fuchsia-virtual-inheritance
google-build-explicit-make-pair
google-build-namespaces
google-build-using-namespace
google-default-arguments
google-explicit-constructor
google-global-names-in-headers
google-objc-avoid-nsobject-new
google-objc-avoid-throwing-exception
google-objc-function-naming
google-objc-global-variable-declaration
google-readability-avoid-underscore-in-googletest-name
google-readability-braces-around-statements
google-readability-casting
google-readability-function-size
google-readability-namespace-comments
google-readability-todo
google-runtime-int
google-runtime-operator
google-runtime-references
google-upgrade-googletest-case
hicpp-avoid-c-arrays
hicpp-avoid-goto
hicpp-braces-around-statements
hicpp-deprecated-headers
hicpp-exception-baseclass
hicpp-explicit-conversions
hicpp-function-size
hicpp-invalid-access-moved
hicpp-member-init
hicpp-move-const-arg
hicpp-multiway-paths-covered
hicpp-named-parameter
hicpp-new-delete-operators
hicpp-no-array-decay
hicpp-no-assembler
hicpp-no-malloc
hicpp-noexcept-move
hicpp-signed-bitwise
hicpp-special-member-functions
hicpp-static-assert
hicpp-undelegated-constructor
hicpp-uppercase-literal-suffix
hicpp-use-auto
hicpp-use-emplace
hicpp-use-equals-default
hicpp-use-equals-delete
hicpp-use-noexcept
hicpp-use-nullptr
hicpp-use-override
hicpp-vararg
linuxkernel-must-check-errs
llvm-header-guard
llvm-include-order
llvm-namespace-comment
llvm-prefer-isa-or-dyn-cast-in-conditionals
llvm-prefer-register-over-unsigned
llvm-qualified-auto
llvm-twine-local
misc-definitions-in-headers
misc-misplaced-const
misc-new-delete-overloads
misc-non-copyable-objects
misc-non-private-member-variables-in-classes
misc-redundant-expression
misc-static-assert
misc-throw-by-value-catch-by-reference
misc-unconventional-assign-operator
misc-uniqueptr-reset-release
misc-unused-alias-decls
misc-unused-parameters
misc-unused-using-decls
modernize-avoid-bind
modernize-avoid-c-arrays
modernize-concat-nested-namespaces
modernize-deprecated-headers
modernize-deprecated-ios-base-aliases
modernize-loop-convert
modernize-make-shared
modernize-make-unique
modernize-pass-by-value
modernize-raw-string-literal
modernize-redundant-void-arg
modernize-replace-auto-ptr
modernize-replace-random-shuffle
modernize-return-braced-init-list
modernize-shrink-to-fit
modernize-unary-static-assert
modernize-use-auto
modernize-use-bool-literals
modernize-use-default-member-init
modernize-use-emplace
modernize-use-equals-default
modernize-use-equals-delete
modernize-use-nodiscard
modernize-use-noexcept
modernize-use-nullptr
modernize-use-override
modernize-use-trailing-return-type
modernize-use-transparent-functors
modernize-use-uncaught-exceptions
modernize-use-using
mpi-buffer-deref
mpi-type-mismatch
objc-avoid-nserror-init
objc-forbidden-subclassing
objc-missing-hash
objc-property-declaration
objc-super-self
openmp-exception-escape
openmp-use-default-none
performance-faster-string-find
performance-for-range-copy
performance-implicit-conversion-in-loop
performance-inefficient-algorithm
performance-inefficient-string-concatenation
performance-inefficient-vector-operation
performance-move-const-arg
performance-move-constructor-init
performance-no-automatic-move
performance-noexcept-move-constructor
performance-trivially-destructible
performance-type-promotion-in-math-fn
performance-unnecessary-copy-initialization
performance-unnecessary-value-param
portability-simd-intrinsics
readability-avoid-const-params-in-decls
readability-braces-around-statements
readability-const-return-type
readability-container-size-empty
readability-convert-member-functions-to-static
readability-delete-null-pointer
readability-deleted-default
readability-else-after-return
readability-function-size
readability-identifier-naming
readability-implicit-bool-conversion
readability-inconsistent-declaration-parameter-name
readability-isolate-declaration
readability-magic-numbers
readability-make-member-function-const
readability-misleading-indentation
readability-misplaced-array-index
readability-named-parameter
readability-non-const-parameter
readability-qualified-auto
readability-redundant-access-specifiers
readability-redundant-control-flow
readability-redundant-declaration
readability-redundant-function-ptr-dereference
readability-redundant-member-init
readability-redundant-preprocessor
readability-redundant-smartptr-get
readability-redundant-string-cstr
readability-redundant-string-init
readability-simplify-boolean-expr
readability-simplify-subscript-expr
readability-static-accessed-through-instance
readability-static-definition-in-anonymous-namespace
readability-string-compare
readability-uniqueptr-delete-release
readability-uppercase-literal-suffix
zircon-temporary-objects

(or here: https://clang.llvm.org/extra/clang-tidy/checks/list.html)

The goal is not to enable them all, as many of them conflict (google vs. llvm, etc), but to just enable the ones or the groups (minus a few) that are useful.

Also, the default checks are pretty safe and probably can be enabled. I partially ran them and don't think there were any changes to (at least the headers) in the current form.

@henryiii
henryiiiforce-pushed the style/clangtidy branch 3 times, most recently from 4704ae8 to 00e4bf2CompareSeptember 12, 2020 12:27
@henryiii

henryiii commented Sep 12, 2020

Copy link
Copy Markdown
CollaboratorAuthor

I think that this solves the occasional output failure on Windows CI (and I think I've seen it in GooFit on Unix) - the destructor was incorrect, as discovered by the default analyzer checks in clang-tidy.

/pybind11/include/pybind11/iostream.h:71:9: warning: Call to virtual method 'pythonbuf::sync' during destruction bypasses virtual dispatch [clang-analyzer-optin.cplusplus.VirtualCall]
sync();
^
/pybind11/tests/test_iostream.cpp:72:5: note: Calling '~scoped_ostream_redirect'
});
^
/pybind11/include/pybind11/iostream.h:118:5: note: Calling '~pythonbuf'
}
^
/pybind11/include/pybind11/iostream.h:71:9: note: Call to virtual method 'pythonbuf::sync' during destruction bypasses virtual dispatch
sync();
^

Here's the page on possible solutions, I chose the private member one, though the fully qualified one might work too. Useful page.

@bstaletic

Copy link
Copy Markdown
Collaborator

I chose the private member one, though the fully qualified one might work too.

That does silence the warning, but it shouldn't have any other effect. When executing the destructor, every member function is treated as if it were non-virtual. All in all, this should have exactly the same assembly output and I'd be very surprised if it has any effect on the flake you're referring to.

@henryiii

henryiii commented Sep 12, 2020

Copy link
Copy Markdown
CollaboratorAuthor

The problem is that the correct destructor is guaranteed to be called, but when it is called (due to destruction order), the vtable for the base is loaded, not the child. So ->sync() is the base's sync, not the child's sync. By skipping the table when calling sync, then it calls the correct one. This is exactly the GooFit bug / CI flake bug we see - the destructor does not force a purge; if the CI is loaded down (or pretty much anytime in GooFit that it is working), the destructor does not cause the final output to show up.

(Sync calls virtual functions too, but those are correct in the base. It's the call from the destructor that's a problem.

If it's a clear solution, I could do the fully qualified solution instead if you prefer.

@bstaletic

Copy link
Copy Markdown
Collaborator

the table for the base is loaded, not the child.

This is the part that I wasn't aware, but it makes sense. Thanks for the explanation.

@henryiii

henryiii commented Sep 12, 2020

Copy link
Copy Markdown
CollaboratorAuthor

I wasn't aware of it either, which is why I've been trying to solve this bug (in the context of GooFit, anyway) for over two years...

The fully qualified solution is a little cleaner/clearer, will change to that. (Edit: I think I miss-read, the fully-qualified method was just a way to silence the warning and clarify you wanted to do that).

@henryiii

Copy link
Copy Markdown
CollaboratorAuthor

(It is possible I'm wrong here, but it's still slightly more correct this way, and we can watch for that bug in the future)

@bstaletic

Copy link
Copy Markdown
Collaborator

A simple check says that it's not as broken as you seem to suggest.

https://godbolt.org/z/4fYojh

Notice that b has a static type of Base*, but dynamic type of Derived*. Two observations:

  1. The correct destructor is called, just like you said it would. The destructor of Derived.
  2. Once we're inside that destructor, a call to f()isn't virtual. We're already "inside" Derived and so Derived::f gets called.

Can we come up with a case where "some other f()" gets called?

@henryiii

Copy link
Copy Markdown
CollaboratorAuthor

For pybind11, this only fails on msvc (and NVCC for GooFit). I would rather expect the other compilers already handle this properly.

@YannickJadoulYannickJadoul left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! :-)
I also really like the incremental approach, for these kinds of changes. As far as I'm concerned, we could merge small batches of these in separate PRs, even. That would keep things more easily reviewable.

The only one I'm a bit skeptical about is modernize-use-using. Not sure it that's so problematic. But yes, it's modernizing our code base, so I'm also not opposed.

Personal request: maybe something that flags C-style casts? (cppcoreguidelines-pro-type-cstyle-cast?) Then again, in the context of the Python C API, C-style casts might make sense, ofc, since it is C.

Comment threadinclude/pybind11/cast.h
Comment threadinclude/pybind11/pybind11.h
Comment threadinclude/pybind11/iostream.h
@henryiii

Copy link
Copy Markdown
CollaboratorAuthor

C-style casts are required in a few places, I believe, but it would be good to identify those and replace the others. I tried this briefly (as part of several others - doing many at once leads to conflicts, though). I know the cast I worked on recently was not allowed to be written as a static_cast, since it was converting something like *T to int (or something like that, I don't remember).

I didn't add the NULL/0 -> nullptr because it looked odd to have a nullptr in a C api. :)

Comment threadtests/test_class.cpp Outdated
@YannickJadoul

YannickJadoul commented Sep 13, 2020

Copy link
Copy Markdown
Collaborator

C-style casts are required in a few places, I believe, but it would be good to identify those and replace the others. I tried this briefly (as part of several others - doing many at once leads to conflicts, though). I know the cast I worked on recently was not allowed to be written as a static_cast, since it was converting something like *T to int (or something like that, I don't remember).

Can be in a different PR, though. I can put some effort into that, if you want; not per se expecting you to do it, ofc ;-)

Also: ugh, never realized there's C casts you can't even do with a reinterpret_cast...
EDIT: Nevermind that reinterpret_cast does work, here! :-)

I didn't add the NULL/0 -> nullptr because it looked odd to have a nullptr in a C api. :)

Agreed. That's why I'm a bit hesitant indeed.

@henryiii

Copy link
Copy Markdown
CollaboratorAuthor

Ahh, I think I didn't try reinterpret_cast.

@henryiii

Copy link
Copy Markdown
CollaboratorAuthor

On that page I linked, "has fix" doesn't seem to be accurate. cppcoreguidelines-pro-type-cstyle-cast says it has a fix but doesn't, and google-readability-casting says it doesn't but does. However, (correctly) it only fixes when it's sure (so I think pretty much .cpp files), so this will take some work to convert.

@henryiii

Copy link
Copy Markdown
CollaboratorAuthor

Assuming you run it (the formula is in the PR), try running it a couple of times, it finds a few more fixes the second time. Remember to clean after you run to get it to recompile. And no multithreaded building if you are changing the source with -fix!

@henryiii

henryiii commented Sep 13, 2020

Copy link
Copy Markdown
CollaboratorAuthor

Yes, it's here: https://clang.llvm.org/extra/clang-tidy/checks/readability-qualified-auto.html - that recommends (based on the LLVM style guide) that auto be fully qualified.

For example:

/pybind11/tests/test_callbacks.cpp:108:9: warning: 'auto result' can be declared as 'const auto *result' [readability-qualified-auto]
auto result = f.target<fn_type>();
^~~~~
const auto *
/pybind11/tests/test_callbacks.cpp:108:9: note: FIX-IT applied suggested code changes

I'll add that one too, along with a partial attempt for C++ style casts.

Edit: Nevermind, seems we hide differences in constness between Python 2.7, 3.5, and 3.8 using unqualified auto. Could be be improved a bit, with explicit ignores and notes on the ones that may or not be const, but for now, leaving that check off.

@henryiii
henryiiiforce-pushed the style/clangtidy branch 2 times, most recently from 98da5c4 to 7d86d3bCompareSeptember 13, 2020 14:24
@bstaletic

Copy link
Copy Markdown
Collaborator

Also: ugh, never realized there's C casts you can't even do with a reinterpret_cast...

For the record, C casts are pretty wild. They try, in order:

  1. const_cast<NewType>
  2. static_cast<NewType>, but with relaxed rules
  3. const_cast(static_cast()), now in combination and with relaxed rules
  4. reinterpret_cast<NewType>
  5. const_cast(reinterpret_cast())

For more info see https://en.cppreference.com/w/cpp/language/explicit_cast#Explanation

@wjakob

Copy link
Copy Markdown
Member

Oh wow, that is a nasty issue you've found in iostream.h. Although this code should probably work "as-is" (dispatching sync() to the pybind11-provided class), I can see how this is probably on the fringe of well-defined behavior in a compiler like MSVC. Your workaround with the separate non-virtual function looks good to me.

I have one objection to some of those changes here, and that's the excessive use of "fancy" C++ cast operators. They definitely do have a place in the standard, and that's when working with polymorphic classes, where some casts can lead to undefined behavior, and others will resolve into complex operations that walk through RTTI tables.

But for very basic stuff, like casting something to an int, or casting a void* pointer to a CPython API object, I find this really unreadable to have lots of static_casts over the place. Especially considering the later point, it's worth noting that almost all pointers in pybind11 are CPython API C-style pointers. We have virtually (ha!) no polymorphism within pybind11 itself.

```
/pybind11/include/pybind11/iostream.h:71:9: warning: Call to virtual method 'pythonbuf::sync' during destruction bypasses virtual dispatch [clang-analyzer-optin.cplusplus.VirtualCall]
sync();
^
/pybind11/tests/test_iostream.cpp:72:5: note: Calling '~scoped_ostream_redirect'
});
```
@henryiii

Copy link
Copy Markdown
CollaboratorAuthor

I've dropped that cast operator commit (was always supposed to be completely non controversial), and rebased. Going in now, then!

@henryiii
henryiii merged commit e7bafc8 into pybind:masterSep 15, 2020
@henryiii
henryiii deleted the style/clangtidy branch September 15, 2020 13:57
@henryiii

Copy link
Copy Markdown
CollaboratorAuthor

I can see how this is probably on the fringe of well-defined behavior in a compiler like MSVC. Your workaround with the separate non-virtual function looks good to me.

That's really what it looks like to me. Though I will unconvince myself of this instantly if this fails CI again at some point after the merge. ;)

@rwgk

rwgk commented Sep 15, 2020

Copy link
Copy Markdown
Collaborator

I only got a chance to look here now: massive thanks @henryiii !

Comment threadtests/test_factory_constructors.cpp
Comment threadtests/test_factory_constructors.cpp
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@henryiii@bstaletic@YannickJadoul@wjakob@rwgk