hwright marked 12 inline comments as done. hwright added inline comments.
================ Comment at: clang-tidy/abseil/DurationComparisonCheck.cpp:25 +static llvm::Optional<DurationScale> getScaleForInverse(llvm::StringRef Name) { + static const std::unordered_map<std::string, DurationScale> ScaleMap( + {{"ToDoubleHours", DurationScale::Hours}, ---------------- aaron.ballman wrote: > sammccall wrote: > > aaron.ballman wrote: > > > sammccall wrote: > > > > aaron.ballman wrote: > > > > > sammccall wrote: > > > > > > hwright wrote: > > > > > > > JonasToth wrote: > > > > > > > > hwright wrote: > > > > > > > > > JonasToth wrote: > > > > > > > > > > I think this could be made a `DenseMap` with just the right > > > > > > > > > > amount of dense entries. 12 elements seems not too much to > > > > > > > > > > me. > > > > > > > > > > Does the key-type need to be `std::string`, or could it be > > > > > > > > > > `StringRef`(or `StringLiteral` making everything > > > > > > > > > > `constexpr` if possible)? > > > > > > > > > > Is there some strange stuff with dangling pointers or other > > > > > > > > > > issues going on? > > > > > > > > > Conceptually, this could easily be `constexpr`, but my > > > > > > > > > compiler doesn't seem to want to build something which is > > > > > > > > > called `static constexpr llvm::DenseMap<llvm::StringRef, > > > > > > > > > DurationScale>`. It's chief complaint is that such a type > > > > > > > > > has a non-trivial destructor. Am I using this correctly? > > > > > > > > I honestly never tried to make a `constexpr DenseMap<>` but it > > > > > > > > makes sense it is not possible, as `DenseMap` is involved with > > > > > > > > dynamic memory after all, which is not possible with > > > > > > > > `constexpr` (yet). So you were my test-bunny ;) > > > > > > > > > > > > > > > > I did reread the Data-structures section in the LLVM manual, i > > > > > > > > totally forgot about `StringMap` that maps strings to values. > > > > > > > > `DenseMap` is good when mapping small values to each other, as > > > > > > > > we do here (`const char* -> int (whatever the enum deduces > > > > > > > > too)`), which would fit. > > > > > > > > `StringMap` does allocations for the strings, which we don't > > > > > > > > need. > > > > > > > > > > > > > > > > `constexpr` aside my bet is that `DenseMap` fits this case the > > > > > > > > better, because we can lay every thing out and never touch it > > > > > > > > again. Maybe someone else has better arguments for the decision > > > > > > > > :) > > > > > > > > > > > > > > > > Soooooo: could you please try `static const > > > > > > > > llvm::DenseMap<llvm::StringRef, DurationScale>`? :) > > > > > > > > (should work in theory: https://godbolt.org/z/Qo7Nv4) > > > > > > > `static const llvm::DenseMap<llvm::StringRef, DurationScale>` > > > > > > > works here. :) > > > > > > Sorry for the drive-by... > > > > > > This has a non-trivial destructor, so violates > > > > > > https://llvm.org/docs/CodingStandards.html#do-not-use-static-constructors > > > > > > at least in spirit. > > > > > > > > > > > > Best to leak it (`auto &ScaleMap = *new const > > > > > > llvm::DenseMap<...>(...)`) to avoid the destructor issues. The > > > > > > constructor issues are already handled by making it function-local. > > > > > I do not think this violates the coding standard -- that's talking > > > > > mostly about global objects, not local objects, and is concerned > > > > > about startup performance and initialization orders. I don't see any > > > > > destructor ordering issues with this, so I do not think any of that > > > > > applies here and leaking would be less than ideal. > > > > There are three main issues with global objects that the coding > > > > standard mentions: > > > > - performance when unused. Not relevant to function-local statics, > > > > only constructed when used > > > > - static initialization order fiasco (constructors). Not relevant to > > > > function-local statics, constructed in well-defined order > > > > - static initialization order fiasco (destructors). Just as relevant > > > > to function-local statics as to any other object! > > > > > > > > That's why I say it violates the rule in spirit: the destructor is > > > > global. https://isocpp.org/wiki/faq/ctors#construct-on-first-use-v2 > > > > > > > > > I don't see any destructor ordering issues with this > > > > The reason these constructs are disallowed in coding guidelines is that > > > > humans can't see the problems, and they manifest in non-local ways :-( > > > > > > > > > leaking would be less than ideal > > > > Why? The current code will (usually) destroy the object when the > > > > program exits. This is a waste of CPU, the OS will free the memory. > > > > The reason these constructs are disallowed in coding guidelines is that > > > > humans can't see the problems, and they manifest in non-local ways :-( > > > > > > Can you explain why you think this would have any non-local impact on > > > destruction? The DenseMap is local to the function and a pointer to it > > > never escapes (so nothing can rely on it), and the data contained are > > > StringRefs wrapping string literals and integer values. > > > > > > > Why? The current code will (usually) destroy the object when the > > > > program exits. This is a waste of CPU, the OS will free the memory. > > > > > > Static analysis tools will start barking about memory leaks which then > > > adds noise to the output, hiding real issues. > > > Can you explain why you think this would have any non-local impact on > > > destruction? > > I haven't analyzed this case in detail. A trivial example is running this > > check on a detached thread (sure, don't do that, but that's a non-local > > constraint...). It's possible there are no others here. > > > > > Static analysis tools will start barking about memory leaks which then > > > adds noise to the output, hiding real issues. > > > > This is a common pattern (from C++ FAQ) that tools are IME aware of. The > > object is still reachable so it's not truly a leak. (The heap checkers I > > know of are dynamic, but this is even easier to recognize statically). > > > > (@hwright: sorry for the derail here, I know you're aware of this issue) > I guess I still read the coding standard differently. While it's certainly > possible to have a finalization order fiasco with local statics, I believe > you have to work considerably harder to craft one. I was doing some code > archaeology on the words in the coding standard, and I don't think this was > the scenario we had in mind when crafting that rule, but these words have > been around for quite some time and I may have missed contextual discussion > from the mailing lists. > > It might make sense to propose a patch that adds some clarity to the coding > standard in this area. We use static locals like this in numerous places > (IdentifierNamingCheck.cpp, TypePromotionInMathFnCheck.cpp, > TypeMismatchCheck.cpp, etc) and it would be good to nail down what we want to > have happen in that case. > > As for this code review, I still think using a heap allocation is not a good > approach. If there is a serious concern about finalization ordering, we can > use a `ManagedStatic` instead. From all this discussion, it sounds like leaving the heap allocation as-is is what we want here. Though I also understand an am sad about the potential for finalization ordering, and I'm doubly sad that C++ doesn't give us a good way to create a static map like this which doesn't require //any// finalization. ================ Comment at: clang-tidy/abseil/DurationComparisonCheck.cpp:50 + static const std::unordered_map<DurationScale, + std::tuple<std::string, std::string>> + InverseMap( ---------------- JonasToth wrote: > hwright wrote: > > hwright wrote: > > > JonasToth wrote: > > > > This variable is a little hard to read. Could you make a little > > > > wrapper-struct instead of the `tuple` to make clear which element > > > > represents what? > > > > Otherwise, why not `std::pair`? > > > > > > > > - same `DenseMap` argument > > > > - same `StringRef`/`StringLiteral` instead `string` consideration > > > `std::pair` works here. > > > > > > I'll defer the `DenseMap` and `StringRef`/`StringLiteral` changes until > > > we determine if they are actually possible. > > ...but using `DenseMap` here doesn't work. From the mountains of compiler > > output I get when I try, it appears that `DenseMap` adds some constraints > > on the key type, and an `enum class` doesn't meet those constraints. > > > > fwiw, the errors are mostly of this form: > > ``` > > /llvm/llvm/include/llvm/ADT/DenseMap.h:1277:45: error: ‘getEmptyKey’ is not > > a member of ‘llvm::DenseMapInfo<clang::tidy::abseil::DurationScale>’ > > const KeyT Empty = KeyInfoT::getEmptyKey(); > > ~~~~~~~~~~~~~~~~~~~~~^~ > > /llvm/llvm/include/llvm/ADT/DenseMap.h:1278:53: error: ‘getTombstoneKey’ is > > not a member of ‘llvm::DenseMapInfo<clang::tidy::abseil::DurationScale>’ > > const KeyT Tombstone = KeyInfoT::getTombstoneKey(); > > ~~~~~~~~~~~~~~~~~~~~~~~~~^~ > > /llvm/llvm/include/llvm/ADT/DenseMap.h:1280:44: error: ‘isEqual’ is not a > > member of ‘llvm::DenseMapInfo<clang::tidy::abseil::DurationScale>’ > > while (Ptr != End && (KeyInfoT::isEqual(Ptr[-1].getFirst(), Empty) || > > ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~ > > /llvm/llvm/include/llvm/ADT/DenseMap.h:1281:44: error: ‘isEqual’ is not a > > member of ‘llvm::DenseMapInfo<clang::tidy::abseil::DurationScale>’ > > KeyInfoT::isEqual(Ptr[-1].getFirst(), > > Tombstone))) > > ``` > in order to use `DenseMap` you need to specialize the `DenseMapInfo` for > types, that are not supported yet. More in the llvm manual and by examples. > Storing the integer is no issue, but the `enum class` makes its a type on its > own, same with the pair. > > I believe its fine to leave it an `unordered_map`, even though it might be > slower on access. You can certainly do the extra-mile. I'll just leave this as-in, since it looks like `DenseMapInfo` wants unused sentinel values, which I'm loathe to add to the enum. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D54737/new/ https://reviews.llvm.org/D54737 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits