Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clang 5: std::optional instantiation screws std::is_constructible trait of the parameter type

A really strange and unexpected behaviour of clang 5 was detected when switching to c++17 and replacing custom std::optional solution with the standard one. For some reason, emplace() was being disabled due to faulty evaluation of a std::is_constructible trait of the parameter class.

Some specific preconditions must be satisfied before it reproduces:

#include <optional>

/// Precondition #1: T must be a nested struct
struct Foo
{
    struct Victim
    {
        /// Precondition #2: T must have an aggregate-initializer
        /// for one of its members
        std::size_t value{0};
    };

    /// Precondition #3: std::optional<T> must be instantiated in this scope
    std::optional<Victim> victim;

    bool foo()
    {
        std::optional<Victim> foo;

        // An error
        foo.emplace(); 
        /// Assertion is failed
        static_assert(std::is_constructible<Victim>::value);
    }
};

Live example on godbolt.org


Change any of the preconditions and it compiles as expected. Is there some unknown inconsistency in the standard that makes clang reject this code while being compliant?

As a side note: GCC 7.1 and GCC 7.2 have no problem with the above code.


Bug report at: bugs.llvm.org

like image 984
GreenScape Avatar asked Dec 26 '17 06:12

GreenScape


2 Answers

This looks like a compiler bug. From [class]

A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier.

Which means Victim is complete at std::optional<Victim>, making it no different than any other type in this context.

From [meta]

The predicate condition for a template specialization is_­constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t: T t(declval<Args>()...);

Which is direct-initializing t with arguments of type Args..., or if sizeof...(Args) == 0, it's value-initializing t.

In this case, value-initializing t is to default-initialize t, which is valid hence std::is_constructible_v<Victim> should be true.

With all that said, compilers seems to be struggling a lot compiling this.

like image 83
Passer By Avatar answered Nov 10 '22 00:11

Passer By


Alright, dug up the relevant quotes. The crux of the matter is how std::is_constructible should handle Victim. The most conclusive authority is C++17 (n4659). First [meta.unary.prop/8]:

The predicate condition for a template specialization is_­constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:

T t(declval<Args>()...);

[ Note: These tokens are never interpreted as a function declaration.  — end note ] Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered.

The note I highlighted is not normative (on account of being a note), but it coincides with [temp.variadic]/7:

... When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct, even in cases where omitting the list entirely would otherwise be ill-formed or would result in an ambiguity in the grammar.

So for the purposes of is_­constructible, this T t(); indeed makes t a variable declaration. This initialization is value initialization because [dcl.init/11] says as much:

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

That means that the trait ends up checking if Victim can be value-initialized. Which it may. It's an aggregate, but an implicitly defaulted default c'tor is still defined by the compiler (to support value initialization, obviously).

Long story short. Clang has a bug, you should report it.

like image 3
StoryTeller - Unslander Monica Avatar answered Nov 09 '22 22:11

StoryTeller - Unslander Monica