[Bug c++/90734] New: [concepts] Pre-normalization substitution into constraints of templated function breaks subsumption

Casey at Carter dot net gcc-bugzilla@gcc.gnu.org
Mon Jun 3 17:05:00 GMT 2019


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90734

            Bug ID: 90734
           Summary: [concepts] Pre-normalization substitution into
                    constraints of templated function breaks subsumption
           Product: gcc
           Version: 10.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: Casey at Carter dot net
  Target Milestone: ---

Created attachment 46447
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=46447&action=edit
Repro

Compiling this program:

    template <bool B>
    inline constexpr bool bool_ = B;

    #if defined(WORKAROUND)
    template<class T, class U>
    concept bool Same_impl = __is_same_as(T, U);
    #else
    template <class T, class U>
    concept bool Same_impl = bool_<__is_same_as(T, U)>;
    #endif

    template<class T, class U>
    concept bool Same = Same_impl<T, U> && Same_impl<U, T>;

    template<class T>
    concept bool Foo = Same<const T&, const T&>;

    template<class T>
    concept bool Bar = Foo<T> && Same<T, T>;

    template<class T>
    struct S1 {
        // overload set incorrectly is ambiguous (should resolve to second
overload)
        static constexpr bool f() requires Foo<T> { return false; }
        static constexpr bool f() requires Bar<T> { return true; }
    };

    template<class T>
    struct S2 {
        // overload set incorrectly is not ambiguous (resolves to third
overload)
        static constexpr bool f() requires Foo<T> { return false; }
        static constexpr bool f() requires Bar<T> { return false; }
        static constexpr bool f() requires bool_<true> && true { return true; }
    };

    template<class T>
    concept bool can_f = requires { T::f(); };

    int main() {
        static_assert(Foo<int>);
        static_assert(Bar<int>);

        static_assert(can_f<S1<int>>);  // Fails
        static_assert(S1<int>::f());    // Bogus error

        static_assert(!can_f<S2<int>>); // Fails
    #ifndef WORKAROUND
        static_assert(S2<int>::f());    // Bogus non-error
    #endif
    }

with "-std=c++2a -fconcepts" produces diagnostics:

    /home/casey/casey/Desktop/repro.cpp: In function ‘int main()’:
    /home/casey/casey/Desktop/repro.cpp:43:19: error: static assertion failed
    43 |     static_assert(can_f<S1<int>>);  // Fails
       |                   ^~~~~~~~~~~~~~
    /home/casey/casey/Desktop/repro.cpp:44:30: error: call of overloaded ‘f()’
is ambiguous
    44 |     static_assert(S1<int>::f());    // Bogus error
       |                              ^
    /home/casey/casey/Desktop/repro.cpp:24:27: note: candidate: ‘static
constexpr bool S1<T>::f() requires  Foo<T> [with T = int]’
    24 |     static constexpr bool f() requires Foo<T> { return false; }
       |                           ^
    /home/casey/casey/Desktop/repro.cpp:25:27: note: candidate: ‘static
constexpr bool S1<T>::f() requires  Bar<T> [with T = int]’
    25 |     static constexpr bool f() requires Bar<T> { return true; }
       |                           ^
    /home/casey/casey/Desktop/repro.cpp:46:19: error: static assertion failed
    46 |     static_assert(!can_f<S2<int>>); // Fails
       |                   ^~~~~~~~~~~~~~~

when it should diagnose only the static_assert on line 48. Bar<T> subsumes
Foo<T>, so S1<int>::f should be unambiguous. Conversely, Neither Bar<T> nor
bool_<true> && true subsumes the other, so S2<int>::f should be ambiguous. The
compiler's disagreement with both of these facts suggests premature
substitution replacing bool_<__is_same_as(T, T)> and bool_<__is_same_as(const
T&, const T&)> with bool_<true> *before* determination of subsumption in
overload resolution. That replacement would result in Foo<T> being replaced
with bool_<true> && bool_<true>, and Bar<T> being replaced with bool_<true> &&
bool_<true> && bool_<true> && bool_<true> which *would* produce the observed
behavior during overload resolution.


More information about the Gcc-bugs mailing list