llvm-project icon indicating copy to clipboard operation
llvm-project copied to clipboard

[clang] Potential SFINAE regression with Clang-16

Open NuriAmari opened this issue 2 years ago • 1 comments

Recently while upgrading from Clang 15, to Clang 16 I've come across of a piece of SFINAE that no longer works. I'm not sure if this is a bug, or should never have worked in the first place. Here is a Godbolt demonstrating the issue: https://godbolt.org/z/GbE53Wd3n. Apologies that the repro is somewhat complex, but I couldn't seem to reduce it further.

The crux of the issue is that std::enable_if_t<!is_std_vector<typename LastArg<Args...>::type>, bool> = true> where the last variadic type is some std::vector<T> used to trigger substitution failure, but no longer does. If we write the SFINAE guard std::enable_if_t<is_std_vector<typename LastArg<Args...>::type>, bool> = false> it works. Thanks!

Here is the same sample included in the Godbolt link:

#include <type_traits>
#include <vector>
#include <iostream>
#include <tuple>
#include <map>

// template to get last argument of variadic template
template<typename... Args>
struct LastArg {
  using type =
      typename std::tuple_element<sizeof...(Args) - 1, std::tuple<Args...>>::type;
};
template<>
struct LastArg<> {
  using type = void;
};

// template to check if a type is any specialization of a templated class
template<template<class, class...> class, class>
struct is_instantiation_of : public std::false_type {};
template<template<class, class...> class T, class TInterface>
struct is_instantiation_of<T, T<TInterface>> : public std::true_type {};

// template to check if a type is any specialization of std::vector
template<typename T>
constexpr bool is_std_vector =
    is_instantiation_of<std::vector, T>::value;

// two SFINAE guarded methods, one to be used if the last argument supplied is not a std::vector<T>
// the negation in the SFINAE doesn't work and both functions are considered for a call with std::vector<int>
  template<typename... Args,
           std::enable_if_t<!is_std_vector<typename LastArg<Args...>::type>, bool> =
               true>
//           comparing to false instead of negating with ! seems to work
//           std::enable_if_t<is_std_vector<typename LastArg<Args...>::type>, bool> =
//               false>
void Foo(Args&&... args) {
    std::cout << "Foo";
}

template<typename... Args, typename U>
void Foo(Args&&... args, const std::vector<U>& allocator)
{
    std::cout << "Bar"; 
}

int main() {
    std::vector<int> int_vec;
    Foo(const_cast<const std::vector<int>&>(int_vec));
}

NuriAmari avatar Jun 27 '23 16:06 NuriAmari

@llvm/issue-subscribers-clang-frontend

llvmbot avatar Jun 27 '23 17:06 llvmbot

I am not sure how this ever worked. const vector & is not a specialization of vector. You need to remove the reference somewhere https://godbolt.org/z/EdYYGM8EE

cor3ntin avatar Jul 22 '24 14:07 cor3ntin