Open paulsemel opened 1 year ago
@llvm/issue-subscribers-clang-frontend
Note that GCC and MSVC accept the following, which uses operator.
on T*
in the never-used constexpr branch.
template <typename T>
class UnretainedWrapper {
public:
T* get() const {
// `ptr_` is either a `raw_ptr` or a regular C++ pointer.
if constexpr (IsRawPtrV<StorageType>) {
ptr.ReportIfDangling();
}
return ptr_;
}
using StorageType = T*;
// using StorageType = raw_ptr<T>;
StorageType ptr_;
};
And if not using a type alias, clang accepts the code as well. The StorageType as an alias causes clang to reject the code unlike gcc and MSVC.
This compiles when StorageType is T*
in clang (https://godbolt.org/z/nT13bTrfe):
template <typename T, typename StorageType>
class UnretainedWrapper {
public:
T* get() const {
// `ptr_` is either a `raw_ptr` or a regular C++ pointer.
if constexpr (IsRawPtrV<StorageType>) {
ptr.ReportIfDangling();
}
return ptr_;
}
StorageType ptr_;
};
Here's a more minimal example.
This compiles on clang/gcc/msvc: https://godbolt.org/z/KbGrK1P4f
#include <type_traits>
template <typename T>
class raw_ptr {
public:
T* get() const { return nullptr; }
};
template <typename T>
struct IsRawPtr : std::false_type {};
template <typename T>
struct IsRawPtr<raw_ptr<T>> : std::true_type {};
template <typename T>
inline constexpr bool IsRawPtrV = IsRawPtr<T>::value;
template <typename T, typename StorageType>
class S {
public:
T* get() const {
if constexpr (IsRawPtrV<StorageTypeAlias>) {
// If `ptr_` is `T*` this branch does't happen.
return ptr_.get();
} else {
return ptr_;
}
}
using StorageTypeAlias = StorageType;
StorageTypeAlias ptr_;
};
int main() {
S<int, int*>().get();
S<int, raw_ptr<int>>().get();
return 0;
}
Only Clang rejects this code: https://godbolt.org/z/7xY3sGeM9
#include <type_traits>
template <typename T>
class raw_ptr {
public:
T* get() const { return nullptr; }
};
template <typename T>
struct IsRawPtr : std::false_type {};
template <typename T>
struct IsRawPtr<raw_ptr<T>> : std::true_type {};
template <typename T>
inline constexpr bool IsRawPtrV = IsRawPtr<T>::value;
template <typename T, typename /*StorageType*/>
class S {
public:
T* get() const {
if constexpr (IsRawPtrV<StorageTypeAlias>) {
// If `ptr_` is `T*` this branch does't happen.
return ptr_.get();
} else {
return ptr_;
}
}
using StorageTypeAlias = T*;
StorageTypeAlias ptr_;
};
int main() {
S<int, int*>().get();
S<int, raw_ptr<int>>().get();
return 0;
}
The type of ptr_
is no longer a template parameter of the class. Is gcc and MSVC accepting the code incorrectly?
The following code compiles correctly (https://godbolt.org/z/98dr4bsT8):
Although, this doesn't respect https://en.cppreference.com/w/cpp/language/if:
Changing the
get
method to:will have the expected behaviour.