Extracted from https://reviews.llvm.org/D129781 and address comment: https://reviews.llvm.org/D129781#3655571
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) | That's because of "error: conditional expression is ambiguous; 'llvm::SmallVector<llvm::Constant *, 32>' can be converted to 'ArrayRef<llvm::Constant *>' and vice versa". Need to check if there is easier workaround. |
Instead adding one more constructor another option is tweaking existing SmallVector(const iterator_range<RangeTy> &R) one in following way:
- template <typename RangeTy> - explicit SmallVector(const iterator_range<RangeTy> &R) - : SmallVectorImpl<T>(N) { + template <typename Range, typename = std::enable_if_t<std::is_convertible< + ValueTypeFromRangeType<Range>, T>::value>> + SmallVector(const Range &R) : SmallVectorImpl<T>(N) { this->append(R.begin(), R.end()); }
That would unlock possibility of creating SmallVector not only from ArrayRef but from every range as long as types are convertible.
I didn't explore this way further, I'm not sure but I believe it should work. It's more than just "adding ArrayRef constructor" but if you are interested I can check where it leads.
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) | Would making the ctor explicit help? |
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) | Nope :( Making constructor explicit disables implicit conversion so we cannot do things like: SmallVector<int, 16> NewMask = Mask; anymore. |
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) | And leaving it implicit hides the fact of possible memory allocation, which is not cheap. I think absense of such constructor is by-design. Making it explicit is making it redundant, because there is already a constructor which accepts begin / end, and one that accepts range (note that it is explicit!). It won't save on typing, either. I'm not in favor of this patch, but my word does not count much, so I won't block it. I'd suggest you, however, to request review of core maintainers. |
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) |
I think that's fine. Similar to the existing iterator_range constructor, we would require SmallVector<int, 16> NewMask(Mask), which seems like the idiomatic way to write it anyway?
It is not redundant. It ensures that iterator_range and ArrayRef can be freely substituted. Switching iterator_range to ArrayRef currently requires going through a lot of SmallVector constructors and replacing them with less readable code. The alternative to this change is D129988, which looks like a significant regression in code quality. |
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) | Please also consider the fact that this is API breaking change due to this "conditional expression is amiguous" error. Many external projects depend on LLVM/ADT, and all of them will have to adapt this change. |
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) |
Ok, makes sense. |
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) |
You are right, explicit works when we use brackets instead assignment. I'm gonna add it to constructor and adjust rest of patch. |
Update constructor definition with explicit to avoid implicit conversions and remove ambiguity workarounds.
Adjust constructor users and add unit tests.
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) | @barannikov88: after addressing @nikic comment there is no API breaking change anymore. As far I can tell SmallVector API is only extended and still backward-compatible. Hope it looks better now. |
clang/lib/CodeGen/CGExprConstant.cpp | ||
---|---|---|
504 ↗ | (On Diff #446472) | Looks good, thank you. Still, I would suggest requesting a review from more experienced developers than me. |
llvm/include/llvm/ADT/SmallVector.h | ||
---|---|---|
35 | Should be typename T. class T is archaic. |
I think my only concern with this change is that it will also allow some implicit ArrayRef constructors. For example, this will permit creating a SmallVector from std::array, std::vector, or just T -- but only if ArrayRef is in scope. This seems somewhat dangerous.
I'm not sure if C++ provides any good way to avoid that, short of explicitly marking certain constructors as deleted?
I'm wondering if it might not be better to make this a fully generic overload that accepts any range (i.e., anything with begin() and end()). Paradoxically, this will likely be less permissive in practice than having explicit iterator_range and ArrayRef overloads, because it allows less implicit conversions.
Maybe I'm missing something, but is it right? It appears that only one implicit conversion in constructor is allowed: https://stackoverflow.com/questions/63320366/double-implicit-conversion-in-c
I think going from vector/array to SmallVector is ill-formed unless we explicitly create temporary ArrayRef in-between: https://godbolt.org/z/Geqbajf1x
Rebase and fix last comment. Remove dependency to https://reviews.llvm.org/D129781 by moving relevant part of change here.
Looks like a nice improvement, thanks!
llvm/unittests/ADT/SmallVectorTest.cpp | ||
---|---|---|
869–870 | This seems like an unrelated change unless I am missing something? If so, this could likely be pulled out and just submitted separately as NFC |
llvm/unittests/ADT/SmallVectorTest.cpp | ||
---|---|---|
869–870 | Addressed here: https://reviews.llvm.org/D131173 |
Should be typename T. class T is archaic.