Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

__is_constructible intrinsic does not SFINAE on default member initializers #44503

Closed
AlisdairM mannequin opened this issue Mar 9, 2020 · 3 comments
Closed

__is_constructible intrinsic does not SFINAE on default member initializers #44503

AlisdairM mannequin opened this issue Mar 9, 2020 · 3 comments
Labels
bugzilla Issues migrated from bugzilla c++ invalid Resolved as invalid, i.e. not a bug

Comments

@AlisdairM
Copy link
Mannequin

AlisdairM mannequin commented Mar 9, 2020

Bugzilla Link 45158
Resolution INVALID
Resolved on Sep 11, 2020 18:30
Version 9.0
OS Linux
CC @faserg1,@DougGregor,@zygoloid

Extended Description

The various standard type traits querying whether a type is constructible are implemented (in both libc++ and libstdc++) as ultimately delegating to an intrinsic such as __is_constructible. This trait fails to account for default member initializers, which produce a hard error rather than returning a true/false answer when evaluating the intrinsic, and hence the trait.

Example:

#include <type_traits>

template <class T>
struct Wrap {
    Wrap() = default;

    T data{};    // default member initializer
};

struct NoDefault {
    NoDefault(NoDefault const&) {}  // non-trivial, not an aggregate
};

int main() {
    using namespace std;
    static_assert(!is_default_constructible<Wrap<NoDefault>>::value, "bad");
}

Godbolt link for quick experimentation: https://godbolt.org/z/-D9mpA

This fails with both libc++ and libstdc++, for all dialects of C++11 and later, for all online compilers up to and including trunk.

@zygoloid
Copy link
Mannequin

zygoloid mannequin commented Mar 9, 2020

SFINAE only applies to instantiating the declaration of a function template specialization (and a few other things not relevant here). It does not apply to triggering the implicit definition of a defaulted constructor, nor to determining whether a defaulted constructor would be implicitly deleted.

It doesn't appear to me that Clang is doing anything wrong here.

If you want this case to become valid, I think the right core issue to file would be to suggest that [class.default.ctor]/2 should say that a defaulted default constructor is defined as deleted if it's in a templated class and instantiation of any default member initializer results in an invalid type or expression in the immediate context of the instantiation. (That is, add a brand new kind of SFINAE for this case.) CWG2202 is doing something similar for default arguments, so this is not unprecedented.

@faserg1
Copy link
Mannequin

faserg1 mannequin commented Sep 11, 2020

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96645
Is there a same bug as in GCC?

Tried to compile this code:

template
struct bool_constant
{
static constexpr bool value = B;
using type = bool_constant;
};

using true_type = bool_constant;

template
struct is_default_constructible
: bool_constant<__is_constructible(T)>
{ };

void testVarStruct()
{
struct DataWithStruct {
struct A {
int number = 5; // compiles, if remove initialization
};

    is_default_constructible<A>::type t = true_type{};
};

}

Output:

:22:47: error: no viable conversion from 'bool_constant' to 'bool_constant'
    is_default_constructible<A>::type t = true_type{};

@zygoloid
Copy link
Mannequin

zygoloid mannequin commented Sep 12, 2020

Re comment#2, that's not the same as this issue.

Default member initializers are not parsed until the closing brace of the outermost enclosing class they're nested within -- the default member initializer for A is not parsed until we reach the end of DataWithStruct. As a consequence (in particular, because we can't compute the exception specification for the default constructor until we've parsed the default member initializers, and in general we never form a call to a function until after we know its exception specification), default construction of an A fails in the context of the type of DataWithStruct::t.

So that's not a bug.

Given the original problem is also not a bug, I'm going to resolve this INVALID. We can reopen it or open a new bug if the committee decides to do something SFINAE-like for default member initializers.

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 10, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla c++ invalid Resolved as invalid, i.e. not a bug
Projects
None yet
Development

No branches or pull requests

0 participants