blob: 5ea2642e415d9a1416d9ddf3e034f308e9f2bd83 [file] [log] [blame]
// { dg-do compile { target c++11 } }
// Minimized from the testcase for PR c++/88146,
// then turned into multiple variants.
// We issue an error when calling an inherited ctor with at least one
// argument passed through a varargs ellipsis, if the call is in an
// evaluated context. Even in nonevaluated contexts, we will
// instantiate constexpr templates (unlike non-constexpr templates),
// which might then issue errors that in nonevlauated contexts
// wouldn't be issued.
// In these variants, the inherited ctor is constexpr, but it's only
// called in unevaluated contexts, so no error is issued. The
// templateness of the ctor doesn't matter, because the only call that
// passes args through the ellipsis is in a noexcept expr, that is not
// evaluated. The ctors in derived classes are created and
// instantiated, discarding arguments passed through the ellipsis when
// calling base ctors, but that's not reported: we only report a
// problem when *calling* ctors that behave this way.
namespace unevaled_call {
namespace no_arg_before_ellipsis {
namespace without_template {
struct foo {
constexpr foo(...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
using boo::boo;
};
void f() noexcept(noexcept(bar{0}));
}
namespace with_template {
struct foo {
template <typename... T>
constexpr foo(...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
using boo::boo;
};
void f() noexcept(noexcept(bar{0}));
}
}
namespace one_arg_before_ellipsis {
namespace without_template {
struct foo {
constexpr foo(int, ...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
using boo::boo;
};
void f() noexcept(noexcept(bar{0,1}));
}
namespace with_template {
struct foo {
template <typename T>
constexpr foo(T, ...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
using boo::boo;
};
void f() noexcept(noexcept(bar{0,1}));
}
}
}
// In these variants, the inherited ctor is constexpr, and it's called
// in unevaluated contexts in ways that would otherwise trigger the
// sorry message. Here we check that the message is not issued at
// those calls, nor at subsequent calls that use the same ctor without
// passing arguments through its ellipsis. We check that it is issued
// later, when we pass the ctor arguments through the ellipsis.
namespace evaled_bad_call_in_u {
namespace one_arg_before_ellipsis {
namespace without_template {
struct foo {
constexpr foo(int, ...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
using boo::boo;
};
void f() noexcept(noexcept(bar{0, 1}));
bar t(0);
bar u(0, 1); // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
}
namespace with_template {
struct foo {
template <typename T>
constexpr foo(T, ...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
using boo::boo;
};
void f() noexcept(noexcept(bar{0,1}));
bar t(0);
bar u(0,1); // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
}
}
namespace no_arg_before_ellipsis {
namespace without_template {
struct foo {
constexpr foo(...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
using boo::boo;
};
void f() noexcept(noexcept(bar{0}));
bar u(0); // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
}
namespace with_template {
struct foo {
template <typename... T>
constexpr foo(...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
using boo::boo;
};
void f() noexcept(noexcept(bar{0}));
bar u(0); // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
}
}
}
// Now, instead of instantiating a class that uses a derived ctor, we
// introduce another template ctor that will use the varargs ctor to
// initialize its base class. The idea is to verify that the error
// message is issued, even if the instantiation occurs in a
// nonevaluated context, e.g., for constexpr templates. In the
// inherited_derived_ctor, we check that even an inherited ctor of a
// constexpr ctor is instantiated and have an error message issued.
namespace derived_ctor {
namespace direct_derived_ctor {
namespace constexpr_noninherited_ctor {
struct foo {
template <typename T>
constexpr foo(T, ...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
template <typename ...T>
constexpr bar(T ... args) : boo(args...) {}
};
void f() noexcept(noexcept(bar{0,1}));
}
namespace no_constexpr_noninherited_ctor {
struct foo {
template <typename T>
constexpr foo(T, ...) {}
};
struct boo : foo {
using foo::foo;
};
struct bar : boo {
template <typename ...T>
/* constexpr */ bar(T ... args) : boo(args...) {}
};
void f() noexcept(noexcept(bar{0,1}));
}
}
namespace inherited_derived_ctor {
namespace constexpr_noninherited_ctor {
struct foo {
template <typename T>
constexpr foo(T, ...) {}
};
struct boo : foo {
using foo::foo;
};
struct bor : boo {
template <typename ...T>
constexpr bor(T ... args) : boo(args...) {}
};
struct bar : bor {
using bor::bor;
};
void f() noexcept(noexcept(bar{0,1}));
}
namespace no_constexpr_noninherited_ctor {
struct foo {
template <typename T>
constexpr foo(T, ...) {}
};
struct boo : foo {
using foo::foo;
};
struct bor : boo {
template <typename ...T>
/* constexpr */ bor(T ... args) : boo(args...) {}
};
struct bar : bor {
using bor::bor;
};
void f() noexcept(noexcept(bar{0,1}));
}
}
}