Bug 1710235 Comment 21 Edit History

Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.

The difference between GCC 11 and GCC 10 / Clang seems to boil down to whether it accepts a templated constructor as a move constructor.

Here's the code example reduced further to illustrate this:

```
#include <utility>  // for std::move

struct NonMovable {
  NonMovable(NonMovable &&) = delete;
};

template <class T>
struct Maybe {
  NonMovable mMember;

  template <typename U>
  Maybe(Maybe<U>&&);
};

void foo(Maybe<int>);

void unlucky(Maybe<int>&& x) {
  foo(std::move(x));
}
```

Here, `NonMovable` has a deleted move constructor, so the default move constructor that the compiler would generate for `Maybe`  is ill-formed. However, `Maybe` has a templated constructor which would be a match for the move if instantiated.

It looks like GCC 10 and Clang don't try to generated a move constructor for `Maybe`, they just use the templated one. GCC 11, however, ignores the templated constructor and tries to generate the default move constructor, which is then ill-formed.

The connection between the original testcase and this one is:
  * `Maybe<nsTHashtable::EntryHandle>::Union` is not movable because it has a member of type `nsTHashtable::EntryHandle` which has a **non-trivial** move constructor.
  * The attempt to move an object of type `Maybe<EntryHandle>` comes from the internals of `invoke_result`, which tries to check if the lambda (which has an `auto` parameter and therefore accepts its argument **by value**) is callable with an argument of type `Maybe<EntryHandle>&&`. The lambda's call operator is instantiated to have the signature `void (Maybe<EntryHandle> maybeEntryHandle)`, and then calling that with `Maybe<EntryHandle>&&` then requires calling the move constructor of `Maybe<EntryHandle>`.

I'm not sure yet which compiler is right. Will leave the needinfo on me until I can get some more info on that.
The difference between GCC 11 and GCC 10 / Clang seems to boil down to whether it accepts a templated constructor as a move constructor.

Here's the code example reduced further to illustrate this:

```
#include <utility>  // for std::move

struct NonMovable {
  NonMovable(NonMovable &&) = delete;
};

template <class T>
struct Maybe {
  NonMovable mMember;

  template <typename U>
  Maybe(Maybe<U>&&);
};

void foo(Maybe<int>);

void unlucky(Maybe<int>&& x) {
  foo(std::move(x));
}
```

Here, `NonMovable` has a deleted move constructor, so the default move constructor that the compiler would generate for `Maybe`  is ill-formed. However, `Maybe` has a templated constructor which would be a match for the move if instantiated.

It looks like GCC 10 and Clang don't try to generate a move constructor for `Maybe`, they just use the templated one. GCC 11, however, ignores the templated constructor and tries to generate the default move constructor, which is then ill-formed.

The connection between the original testcase and this one is:
  * `Maybe<nsTHashtable::EntryHandle>::Union` is not movable because it has a member of type `nsTHashtable::EntryHandle` which has a **non-trivial** move constructor.
  * The attempt to move an object of type `Maybe<EntryHandle>` comes from the internals of `invoke_result`, which tries to check if the lambda (which has an `auto` parameter and therefore accepts its argument **by value**) is callable with an argument of type `Maybe<EntryHandle>&&`. The lambda's call operator is instantiated to have the signature `void (Maybe<EntryHandle> maybeEntryHandle)`, and then calling that with `Maybe<EntryHandle>&&` then requires calling the move constructor of `Maybe<EntryHandle>`.

I'm not sure yet which compiler is right. Will leave the needinfo on me until I can get some more info on that.
The difference between GCC 11 and GCC 10 / Clang seems to boil down to whether it accepts a templated constructor as a move constructor.

Here's the code example reduced further to illustrate this:

```
#include <utility>  // for std::move

struct NonMovable {
  NonMovable(NonMovable &&) = delete;
};

template <class T>
struct Maybe {
  NonMovable mMember;

  template <typename U>
  Maybe(Maybe<U>&&);
};

void foo(Maybe<int>);

void unlucky(Maybe<int>&& x) {
  foo(std::move(x));
}
```

Here, `NonMovable` has a deleted move constructor, so the default move constructor that the compiler would generate for `Maybe`  is ill-formed. However, `Maybe` has a templated constructor which would be a match for the move if instantiated.

It looks like GCC 10 and Clang don't try to generate a move constructor for `Maybe`, they just use the templated one. GCC 11, however, ignores the templated constructor and tries to generate the default move constructor, which is then ill-formed.

The connection between the original testcase and this one is:
  * `Maybe<nsTHashtable::EntryHandle>::Union` is not movable because it has a member of type `nsTHashtable::EntryHandle` which has a **non-trivial** move constructor.
  * The attempt to move an object of type `Maybe<EntryHandle>` comes from the internals of `invoke_result`, which tries to check if the lambda (which has an `auto` parameter and therefore accepts its argument **by value**) is callable with an argument of type `Maybe<EntryHandle>&&`. The lambda's call operator is instantiated to have the signature `void (Maybe<EntryHandle>)`, and then calling that with `Maybe<EntryHandle>&&` then requires calling the move constructor of `Maybe<EntryHandle>`.

I'm not sure yet which compiler is right. Will leave the needinfo on me until I can get some more info on that.

Back to Bug 1710235 Comment 21