Bug 109683 - [13/14/15 Regression] False cyclic dependency error reported for constraint
Summary: [13/14/15 Regression] False cyclic dependency error reported for constraint
Status: ASSIGNED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 13.1.0
: P2 normal
Target Milestone: 13.4
Assignee: Patrick Palka
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2023-05-01 09:43 UTC by Daniel Bertalan
Modified: 2024-05-21 09:14 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-05-01 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Bertalan 2023-05-01 09:43:36 UTC
The following code (reduced from a custom std::variant-like type) compiles with GCC 12.2 and Clang, but is rejected by GCC 13+:

```
template <typename T, typename>
struct VariantConstructors {
  VariantConstructors(T&& t)
    requires(requires { T(t); });
};

template <typename... Ts>
struct InheritFromEntries : Ts... {};
 
template <typename... Ps>
struct InheritFromPack: InheritFromEntries<Ps>... {
  using InheritFromEntries<Ps>::InheritFromEntries...;
};

template <typename... Ts>
struct Variant : InheritFromPack<VariantConstructors<Ts, Variant<Ts...>>...> {};

template <typename T>
class Outer;
struct Inner {
    Inner(Outer<int>);
};
template<typename T>
class Outer {
    Variant<T, Inner> value_;
};

struct Empty {};
void fn(Outer<Empty> x) {}
```

The following compiler error is produced (arguments: -std=c++20):

<source>: In instantiation of 'VariantConstructors<T, <template-parameter-1-2> >::VariantConstructors(T&&) requires requires{T(VariantConstructors<T, <template-parameter-1-2> >::__ct ::t);} [with T = Inner; <template-parameter-1-2> = Variant<int, Inner>]':
<source>:12:51:   required from 'struct InheritFromPack<VariantConstructors<int, Variant<int, Inner> >, VariantConstructors<Inner, Variant<int, Inner> > >'
<source>:16:8:   required from 'struct Variant<int, Inner>'
<source>:25:23:   required from 'class Outer<int>'
<source>:4:25:   required from 'VariantConstructors<T, <template-parameter-1-2> >::VariantConstructors(T&&) requires requires{T(VariantConstructors<T, <template-parameter-1-2> >::__ct ::t);} [with T = Inner; <template-parameter-1-2> = Variant<Empty, Inner>]'
<source>:12:51:   required from 'struct InheritFromPack<VariantConstructors<Empty, Variant<Empty, Inner> >, VariantConstructors<Inner, Variant<Empty, Inner> > >'
<source>:16:8:   required from 'struct Variant<Empty, Inner>'
<source>:25:23:   required from 'class Outer<Empty>'
<source>:29:23:   required from here
<source>:3:3:   required by the constraints of 'template<class T, class> VariantConstructors<T, <template-parameter-1-2> >::VariantConstructors(T&&) requires requires{T(VariantConstructors<T, <template-parameter-1-2> >::__ct ::t);}'
<source>:4:14:   in requirements  [with T = Inner]
<source>:4:14: error: satisfaction of atomic constraint 'requires{T(VariantConstructors<T, <template-parameter-1-2> >::__ct ::t);} [with T = T]' depends on itself
    4 |     requires(requires { T(t); });
      |             ~^~~~~~~~~~~~~~~~~~~

Reproducer on Compiler Explorer: https://godbolt.org/z/TbcoanG5T
The non-reduced preprocessed source can be found here: https://godbolt.org/z/69dMMoWKh
Comment 1 Ali Mohammad Pur Fard 2023-05-01 11:23:35 UTC
As a note, using a type alias for `T` in the constraint seems to work around the issue:
```
template <typename T, typename>
struct VariantConstructors {
  using U = T;
  VariantConstructors(T&& t)
    requires(requires { U(t); });
};

// Everything below is the same as the repro case

template <typename... Ts>
struct InheritFromEntries : Ts... {};
 
template <typename... Ps>
struct InheritFromPack: InheritFromEntries<Ps>... {
  using InheritFromEntries<Ps>::InheritFromEntries...;
};

template <typename... Ts>
struct Variant : InheritFromPack<VariantConstructors<Ts, Variant<Ts...>>...> {};

template <typename T>
class Outer;
struct Inner {
    Inner(Outer<int>);
};
template<typename T>
class Outer {
    Variant<T, Inner> value_;
};

struct Empty {};
void fn(Outer<Empty> x) {}
```

(https://godbolt.org/z/1GTrjcvG6)
Comment 2 Patrick Palka 2023-05-01 11:58:21 UTC
Thanks for the bug report.  Started with r13-980-gdf4f95dbd4764f which made function parameters used in a constraint no longer induce a dependency on all contextual template parameters.

So another workaround would be to make the constructor's constraints explicitly depend on the second template parameter of the class:

template <typename T, typename U>
struct VariantConstructors {
  VariantConstructors(T&& t)
    requires(requires { T(t); typename U; });
};

...
Comment 3 Richard Biener 2023-07-27 09:26:00 UTC
GCC 13.2 is being released, retargeting bugs to GCC 13.3.
Comment 4 Jakub Jelinek 2024-05-21 09:14:54 UTC
GCC 13.3 is being released, retargeting bugs to GCC 13.4.