Ping. On Wed, Oct 03, 2018 at 07:11:37PM -0400, Marek Polacek wrote: > On Wed, Oct 03, 2018 at 10:24:52AM -0400, Jason Merrill wrote: > > On Tue, Oct 2, 2018 at 5:25 PM Marek Polacek <pola...@redhat.com> wrote: > > > > > > On Mon, Oct 01, 2018 at 07:47:10PM -0400, Jason Merrill wrote: > > > > On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <pola...@redhat.com> wrote: > > > > > > > > > > This patch implements C++20 explicit(bool), as described in: > > > > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>. > > > > > > > > > > I tried to follow the noexcept specifier implementation where I > > > > > could, which > > > > > made the non-template parts of this fairly easy. To make > > > > > explicit(expr) work > > > > > with dependent expressions, I had to add DECL_EXPLICIT_SPEC to > > > > > lang_decl_fn, > > > > > which serves as a vessel to get the explicit-specifier to > > > > > tsubst_function_decl > > > > > where I substitute the dependent arguments. > > > > > > > > What's the impact of that on memory consumption? I'm nervous about > > > > adding another word to most functions when it's not useful to most of > > > > them. For several similar things we've been using hash tables on the > > > > side. > > > > > > Yeah, that is a fair concern. I'm not sure if I know of a good way to > > > measure > > > it. I took wide-int.ii and ran /usr/bin/time -v ./cc1plus; then it's > > > roughly > > > Maximum resident set size (kbytes): 95020 > > > vs. > > > Maximum resident set size (kbytes): 95272 > > > which doesn't seem too bad but I don't know if it proves anything. > > > > > > If we went with the hash table, would it work like this? > > > 1) have a hash table mapping decls (key) to explicit-specifiers > > > 2) instead of setting DECL_EXPLICIT_SPEC put the parsed explicit-specifier > > > into the table > > > 3) in tsubst_function_decl look if the fn decl is associated with any > > > explicit-specifier, if it is, substitute it, and set > > > DECL_NONCONVERTING_P > > > accordingly. > > > > Yes. I think you want to use tree_cache_map so you don't have to > > worry about removing entries from the table if the decl is GC'd. > > Done (along with the bit idea). > > > > > > +/* Create a representation of the explicit-specifier with > > > > > + constant-expression of EXPR. COMPLAIN is as for tsubst. */ > > > > > + > > > > > +tree > > > > > +build_explicit_specifier (tree expr, tsubst_flags_t complain) > > > > > +{ > > > > > + if (processing_template_decl && value_dependent_expression_p > > > > > (expr)) > > > > > + /* Wait for instantiation. tsubst_function_decl will take care > > > > > of it. */ > > > > > + return expr; > > > > > + > > > > > + expr = perform_implicit_conversion_flags (boolean_type_node, expr, > > > > > + complain, LOOKUP_NORMAL); > > > > > + expr = instantiate_non_dependent_expr (expr); > > > > > + expr = cxx_constant_value (expr); > > > > > + return expr; > > > > > +} > > > > > > > > Is there a reason not to use build_converted_constant_expr? > > > > > > build_converted_constant_expr doesn't allow narrowing conversions but we > > > should > > > allow them in explicit-specifier which takes "contextually converted > > > constant > > > expression of type bool", much like in > > > > > > constexpr int foo () { return 42; } > > > static_assert (foo()); > > > > > > Right? > > > > That's what the standard seems to require, but I think that's broken; > > I cc'd you on my email to the reflector. > > Thanks. Let's see how that pans; for now I've changed the patch to use > build_converted_constant_expr and tweaked the tests. > > Bootstrapped/regtested on x86_64-linux, ok for trunk? > > 2018-10-03 Marek Polacek <pola...@redhat.com> > > Implement P0892R2, explicit(bool). > * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_explicit_bool. > > * call.c (add_template_candidate_real): Return if the declaration is > explicit and we're only looking for non-converting constructor. > * cp-tree.h (lang_decl_fn): Add has_dependent_explicit_spec_p bit. > (DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P): New macro. > (build_explicit_specifier, store_explicit_specifier): Declare. > * decl.c (build_explicit_specifier): New function. > * parser.c (cp_parser_decl_specifier_seq): Add explicit_specifier > parameter. Pass it down to cp_parser_function_specifier_opt. > (cp_parser_function_specifier_opt): Add explicit_specifier parameter. > <case RID_EXPLICIT>: Parse C++20 explicit(bool). > (cp_parser_explicit_instantiation): Update call to > cp_parser_function_specifier_opt. > (cp_parser_member_declaration): Have cp_parser_decl_specifier_seq save > the explicit-specifier. Save it using store_explicit_specifier. > (cp_parser_single_declaration): Likewise. > * pt.c (store_explicit_specifier, lookup_explicit_specifier): New. > (tsubst_function_decl): Handle explicit(dependent-expr). > > * g++.dg/cpp2a/explicit1.C: New test. > * g++.dg/cpp2a/explicit10.C: New test. > * g++.dg/cpp2a/explicit11.C: New test. > * g++.dg/cpp2a/explicit2.C: New test. > * g++.dg/cpp2a/explicit3.C: New test. > * g++.dg/cpp2a/explicit4.C: New test. > * g++.dg/cpp2a/explicit5.C: New test. > * g++.dg/cpp2a/explicit6.C: New test. > * g++.dg/cpp2a/explicit7.C: New test. > * g++.dg/cpp2a/explicit8.C: New test. > * g++.dg/cpp2a/explicit9.C: New test. > > * testsuite/20_util/any/cons/explicit.cc: Adjust dg-error. > * testsuite/20_util/pair/cons/explicit_construct.cc: Likewise. > * testsuite/20_util/tuple/cons/explicit_construct.cc: Likewise. > > diff --git gcc/gcc/c-family/c-cppbuiltin.c gcc/gcc/c-family/c-cppbuiltin.c > index 96a6b4dfd2b..b085cf9201f 100644 > --- gcc/gcc/c-family/c-cppbuiltin.c > +++ gcc/gcc/c-family/c-cppbuiltin.c > @@ -955,7 +955,7 @@ c_cpp_builtins (cpp_reader *pfile) > } > if (cxx_dialect > cxx14) > { > - /* Set feature test macros for C++1z. */ > + /* Set feature test macros for C++17. */ > cpp_define (pfile, "__cpp_unicode_characters=201411"); > cpp_define (pfile, "__cpp_static_assert=201411"); > cpp_define (pfile, "__cpp_namespace_attributes=201411"); > @@ -975,6 +975,11 @@ c_cpp_builtins (cpp_reader *pfile) > cpp_define (pfile, "__cpp_structured_bindings=201606"); > cpp_define (pfile, "__cpp_variadic_using=201611"); > } > + if (cxx_dialect > cxx17) > + { > + /* Set feature test macros for C++2a. */ > + cpp_define (pfile, "__cpp_explicit_bool=201806"); > + } > if (flag_concepts) > cpp_define (pfile, "__cpp_concepts=201507"); > if (flag_tm) > diff --git gcc/gcc/cp/call.c gcc/gcc/cp/call.c > index b2ca667c8b4..7003a4a2f50 100644 > --- gcc/gcc/cp/call.c > +++ gcc/gcc/cp/call.c > @@ -3251,6 +3251,12 @@ add_template_candidate_real (struct z_candidate > **candidates, tree tmpl, > goto fail; > } > > + /* Now the explicit specifier might have been deduced; check if this > + declaration is explicit. If it is and we're ignoring non-converting > + constructors, don't add this function to the set of candidates. */ > + if ((flags & LOOKUP_ONLYCONVERTING) && DECL_NONCONVERTING_P (fn)) > + return NULL; > + > if (DECL_CONSTRUCTOR_P (fn) && nargs == 2) > { > tree arg_types = FUNCTION_FIRST_USER_PARMTYPE (fn); > diff --git gcc/gcc/cp/cp-tree.h gcc/gcc/cp/cp-tree.h > index efbdad83966..dfc5c4da7d6 100644 > --- gcc/gcc/cp/cp-tree.h > +++ gcc/gcc/cp/cp-tree.h > @@ -2587,7 +2587,8 @@ struct GTY(()) lang_decl_fn { > unsigned this_thunk_p : 1; > unsigned hidden_friend_p : 1; > unsigned omp_declare_reduction_p : 1; > - unsigned spare : 13; > + unsigned has_dependent_explicit_spec_p : 1; > + unsigned spare : 12; > > /* 32-bits padding on 64-bit host. */ > > @@ -3033,6 +3034,12 @@ struct GTY(()) lang_decl { > #define DECL_PURE_VIRTUAL_P(NODE) \ > (LANG_DECL_FN_CHECK (NODE)->pure_virtual) > > +/* Nonzero for FUNCTION_DECL means that this member function (either > + a constructor or a conversion function) has an explicit specifier > + with a value-dependent expression. */ > +#define DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P(NODE) \ > + (LANG_DECL_FN_CHECK (NODE)->has_dependent_explicit_spec_p) > + > /* True (in a FUNCTION_DECL) if NODE is a virtual function that is an > invalid overrider for a function from a base class. Once we have > complained about an invalid overrider we avoid complaining about it > @@ -6366,6 +6373,7 @@ extern tree cxx_maybe_build_cleanup (tree, > tsubst_flags_t); > extern bool check_array_designated_initializer (constructor_elt *, > unsigned HOST_WIDE_INT); > extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t); > +extern tree build_explicit_specifier (tree, tsubst_flags_t); > > /* in decl2.c */ > extern void record_mangling (tree, bool); > @@ -6762,6 +6770,7 @@ extern bool dguide_name_p (tree); > extern bool deduction_guide_p (const_tree); > extern bool copy_guide_p (const_tree); > extern bool template_guide_p (const_tree); > +extern void store_explicit_specifier (tree, tree); > > /* in repo.c */ > extern void init_repo (void); > diff --git gcc/gcc/cp/decl.c gcc/gcc/cp/decl.c > index 2d9d56ef6e1..7beaeb0f1f2 100644 > --- gcc/gcc/cp/decl.c > +++ gcc/gcc/cp/decl.c > @@ -16554,4 +16554,20 @@ require_deduced_type (tree decl, tsubst_flags_t > complain) > return true; > } > > +/* Create a representation of the explicit-specifier with > + constant-expression of EXPR. COMPLAIN is as for tsubst. */ > + > +tree > +build_explicit_specifier (tree expr, tsubst_flags_t complain) > +{ > + if (processing_template_decl && value_dependent_expression_p (expr)) > + /* Wait for instantiation, tsubst_function_decl will handle it. */ > + return expr; > + > + expr = build_converted_constant_expr (boolean_type_node, expr, complain); > + expr = instantiate_non_dependent_expr (expr); > + expr = cxx_constant_value (expr); > + return expr; > +} > + > #include "gt-cp-decl.h" > diff --git gcc/gcc/cp/parser.c gcc/gcc/cp/parser.c > index a17cc3f23f2..dc708616975 100644 > --- gcc/gcc/cp/parser.c > +++ gcc/gcc/cp/parser.c > @@ -2141,11 +2141,11 @@ static void cp_parser_block_declaration > static void cp_parser_simple_declaration > (cp_parser *, bool, tree *); > static void cp_parser_decl_specifier_seq > - (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *); > + (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *, tree * = > NULL); > static tree cp_parser_storage_class_specifier_opt > (cp_parser *); > static tree cp_parser_function_specifier_opt > - (cp_parser *, cp_decl_specifier_seq *); > + (cp_parser *, cp_decl_specifier_seq *, tree *); > static tree cp_parser_type_specifier > (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, bool, > int *, bool *); > @@ -13516,13 +13516,15 @@ cp_parser_decomposition_declaration (cp_parser > *parser, > 2: one of the decl-specifiers is an enum-specifier or a > class-specifier (i.e., a type definition) > > - */ > + EXPLICIT_SPECIFIER is used in case the explicit-specifier, if any, has > + value-dependent expression. */ > > static void > cp_parser_decl_specifier_seq (cp_parser* parser, > cp_parser_flags flags, > cp_decl_specifier_seq *decl_specs, > - int* declares_class_or_enum) > + int* declares_class_or_enum, > + tree* explicit_specifier) > { > bool constructor_possible_p = !parser->in_declarator_p; > bool found_decl_spec = false; > @@ -13643,7 +13645,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser, > case RID_INLINE: > case RID_VIRTUAL: > case RID_EXPLICIT: > - cp_parser_function_specifier_opt (parser, decl_specs); > + cp_parser_function_specifier_opt (parser, decl_specs, > + explicit_specifier); > break; > > /* decl-specifier: > @@ -13870,12 +13873,17 @@ cp_parser_storage_class_specifier_opt (cp_parser* > parser) > virtual > explicit > > + C++2A Extension: > + explicit(constant-expression) > + > Returns an IDENTIFIER_NODE corresponding to the keyword used. > - Updates DECL_SPECS, if it is non-NULL. */ > + Updates DECL_SPECS, if it is non-NULL. Updates EXPLICIT_SPECIFIER, > + in case an explicit-specifier is found. */ > > static tree > cp_parser_function_specifier_opt (cp_parser* parser, > - cp_decl_specifier_seq *decl_specs) > + cp_decl_specifier_seq *decl_specs, > + tree *explicit_specifier) > { > cp_token *token = cp_lexer_peek_token (parser->lexer); > switch (token->keyword) > @@ -13896,8 +13904,51 @@ cp_parser_function_specifier_opt (cp_parser* parser, > break; > > case RID_EXPLICIT: > - set_and_check_decl_spec_loc (decl_specs, ds_explicit, token); > - break; > + { > + tree id = cp_lexer_consume_token (parser->lexer)->u.value; > + /* If we see '(', it's C++20 explicit(bool). */ > + tree expr; > + if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) > + { > + matching_parens parens; > + parens.consume_open (parser); > + > + /* New types are not allowed in an explicit-specifier. */ > + const char *saved_message > + = parser->type_definition_forbidden_message; > + parser->type_definition_forbidden_message > + = G_("types may not be defined in explicit-specifier"); > + > + if (cxx_dialect < cxx2a) > + pedwarn (token->location, 0, > + "%<explicit(bool)%> only available with -std=c++2a " > + "or -std=gnu++2a"); > + > + /* Parse the constant-expression. */ > + expr = cp_parser_constant_expression (parser); > + > + /* Restore the saved message. */ > + parser->type_definition_forbidden_message = saved_message; > + parens.require_close (parser); > + } > + else > + /* The explicit-specifier explicit without a constant-expression is > + equivalent to the explicit-specifier explicit(true). */ > + expr = boolean_true_node; > + > + /* [dcl.fct.spec] > + "the constant-expression, if supplied, shall be a contextually > + converted constant expression of type bool." */ > + expr = build_explicit_specifier (expr, tf_warning_or_error); > + /* We could evaluate it -- mark the decl as appropriate. */ > + if (expr == boolean_true_node) > + set_and_check_decl_spec_loc (decl_specs, ds_explicit, token); > + else if (explicit_specifier) > + /* The expression was value-dependent. Remember it so that we can > + substitute it later. */ > + *explicit_specifier = expr; > + return id; > + } > > default: > return NULL_TREE; > @@ -16658,7 +16709,8 @@ cp_parser_explicit_instantiation (cp_parser* parser) > if (!extension_specifier) > extension_specifier > = cp_parser_function_specifier_opt (parser, > - /*decl_specs=*/NULL); > + /*decl_specs=*/NULL, > + /*explicit_specifier*/NULL); > } > > /* Look for the `template' keyword. */ > @@ -23568,6 +23620,7 @@ cp_parser_member_declaration (cp_parser* parser) > cp_token *initializer_token_start = NULL; > int saved_pedantic; > bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; > + tree explicit_specifier = NULL_TREE; > > /* Check for the `__extension__' keyword. */ > if (cp_parser_extension_opt (parser, &saved_pedantic)) > @@ -23664,7 +23717,8 @@ cp_parser_member_declaration (cp_parser* parser) > cp_parser_decl_specifier_seq (parser, > CP_PARSER_FLAGS_OPTIONAL, > &decl_specifiers, > - &declares_class_or_enum); > + &declares_class_or_enum, > + &explicit_specifier); > /* Check for an invalid type-name. */ > if (!decl_specifiers.any_type_specifiers_p > && cp_parser_parse_and_diagnose_invalid_type_name (parser)) > @@ -24018,6 +24072,8 @@ cp_parser_member_declaration (cp_parser* parser) > /* If the member was not a friend, declare it here. */ > if (!friend_p) > finish_member_declaration (decl); > + if (decl && explicit_specifier) > + store_explicit_specifier (decl, explicit_specifier); > /* Peek at the next token. */ > token = cp_lexer_peek_token (parser->lexer); > /* If the next token is a semicolon, consume it. */ > @@ -24047,6 +24103,8 @@ cp_parser_member_declaration (cp_parser* parser) > else > decl = finish_fully_implicit_template (parser, decl); > } > + if (decl && explicit_specifier) > + store_explicit_specifier (decl, explicit_specifier); > } > > cp_finalize_omp_declare_simd (parser, decl); > @@ -27326,6 +27384,7 @@ cp_parser_single_declaration (cp_parser* parser, > cp_decl_specifier_seq decl_specifiers; > bool function_definition_p = false; > cp_token *decl_spec_token_start; > + tree explicit_specifier = NULL_TREE; > > /* This function is only used when processing a template > declaration. */ > @@ -27341,7 +27400,8 @@ cp_parser_single_declaration (cp_parser* parser, > cp_parser_decl_specifier_seq (parser, > CP_PARSER_FLAGS_OPTIONAL, > &decl_specifiers, > - &declares_class_or_enum); > + &declares_class_or_enum, > + &explicit_specifier); > if (friend_p) > *friend_p = cp_parser_friend_p (&decl_specifiers); > > @@ -27452,6 +27512,9 @@ cp_parser_single_declaration (cp_parser* parser, > > if (decl && VAR_P (decl)) > check_template_variable (decl); > + > + if (decl && explicit_specifier) > + store_explicit_specifier (decl, explicit_specifier); > } > > /* Look for a trailing `;' after the declaration. */ > diff --git gcc/gcc/cp/pt.c gcc/gcc/cp/pt.c > index b8b6545434b..8f838e42eac 100644 > --- gcc/gcc/cp/pt.c > +++ gcc/gcc/cp/pt.c > @@ -12803,6 +12803,28 @@ tsubst_default_arguments (tree fn, tsubst_flags_t > complain) > complain); > } > > +/* Hash table mapping a FUNCTION_DECL to its dependent explicit-specifier. > */ > +static GTY((cache)) tree_cache_map *explicit_specifier_map; > + > +/* Store a pair to EXPLICIT_SPECIFIER_MAP. */ > + > +void > +store_explicit_specifier (tree v, tree t) > +{ > + if (!explicit_specifier_map) > + explicit_specifier_map = tree_cache_map::create_ggc (37); > + DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (v) = true; > + explicit_specifier_map->put (v, t); > +} > + > +/* Lookup an element in EXPLICIT_SPECIFIER_MAP. */ > + > +static tree > +lookup_explicit_specifier (tree v) > +{ > + return *explicit_specifier_map->get (v); > +} > + > /* Subroutine of tsubst_decl for the case when T is a FUNCTION_DECL. */ > > static tree > @@ -12822,6 +12844,17 @@ tsubst_function_decl (tree t, tree args, > tsubst_flags_t complain, > if (!uses_template_parms (DECL_TI_ARGS (t))) > return t; > > + /* Handle explicit(dependent-expr). */ > + if (DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (t)) > + { > + tree spec = lookup_explicit_specifier (t); > + spec = tsubst_copy_and_build (spec, args, complain, in_decl, > + /*function_p=*/false, > + /*i_c_e_p=*/true); > + spec = build_explicit_specifier (spec, complain); > + DECL_NONCONVERTING_P (t) = (spec == boolean_true_node); > + } > + > /* Calculate the most general template of which R is a > specialization. */ > gen_tmpl = most_general_template (DECL_TI_TEMPLATE (t)); > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C > index e69de29bb2d..b39f90f3397 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C > @@ -0,0 +1,63 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +constexpr int fn0 () { return 0; } > +constexpr int fn1 () { return 1; } > + > +struct S { > + explicit(true) S(int); > + explicit(1 == 0) S(int, int); > + explicit(fn0()) S(int, int, int); > + explicit(fn1()) S(int, int, int, int); > +}; > + > +struct X { > + static const bool value = true; > + static constexpr bool foo () { return 1; } > +}; > + > +struct T { > + explicit(true ? 1 : throw 1) T(int); > + explicit(true || true ? 1 : throw 1) T(int, int); > + explicit(X::value) T(int, int, int); > + explicit(X::foo ()) T(int, int, int, int); > +}; > + > +struct W { > + constexpr operator bool() { return true; }; > +}; > + > +struct W2 { > + constexpr operator bool() { return false; }; > +}; > + > +struct U { > + explicit(W()) U(int); > + explicit(W2()) U(int, int); > +}; > + > +int > +main () > +{ > + S s1 = { 1 }; // { dg-error "converting" } > + S s1x{ 1 }; > + S s2 = { 2, 3 }; > + S s3 = { 4, 5, 6 }; > + S s4 = { 7, 8, 9, 10 }; // { dg-error "converting" } > + S s4x{ 7, 8, 9, 10 }; > + > + T t1 = { 1 }; // { dg-error "converting" } > + T t2 = { 1, 2 }; // { dg-error "converting" } > + T t3 = { 1, 2, 3 }; // { dg-error "converting" } > + T t4 = { 1, 2, 3, 4 }; // { dg-error "converting" } > + T t5{ 1 }; > + T t6{ 1, 2 }; > + T t7{ 1, 2, 3 }; > + T t8{ 1, 2, 3, 4 }; > + > + U u1 = { 1 }; // { dg-error "converting" } > + U u2{ 1 }; > + U u3 = { 1, 2 }; > + U u4 { 1, 2 }; > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C > index e69de29bb2d..c8701551c9a 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C > @@ -0,0 +1,32 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +#include <type_traits> > + > +class A {}; > +class B : public A {}; > +class C {}; > +class D { public: operator C() { return c; } C c; }; > + > +template <typename T1, typename T2> > +struct S { > + explicit(!std::is_convertible_v<T1, T2>) > + S(T1, T2) { } > +}; > + > +void > +foo () > +{ > + A a; > + B b; > + C c; > + D d; > + > + S<int, int> s{ 1, 2 }; > + S<int, int> s2 = { 1, 2 }; > + S<B*, A*> s3 = { &b, &a }; > + S<A*, B*> s4 = { &a, &b }; // { dg-error "converting" } > + S<B*, C*> s5 = { &b, &c }; // { dg-error "converting" } > + S<D, C> s6 = { d, c }; > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C > index e69de29bb2d..ad1bed5b3f0 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C > @@ -0,0 +1,29 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a -pedantic" } > + > +template<typename T> > +struct A { > + explicit A(const T&, ...) noexcept; > + A(T&&, ...); > +}; > + > +int i; > +A a1 = { i, i }; // { dg-error "deduction|cannot" } > +A a2{ i, i }; > +A a3{ 0, i }; > +A a4 = { 0, i }; > + > +template<typename T> A(const T&, const T&) -> A<T&>; > +template<typename T> explicit A(T&&, T&&) -> A<T>; > + > +A a5 = { 0, 1 }; // { dg-error "deduction|ambiguous" } > +A a6{ 0, 1 }; > + > +template<typename T> > +struct B { > + template<typename U> using TA = T; > + template<typename U> B(U, TA<U>); > +}; > + > +B b{(int *)0, (char *)0}; > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit2.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit2.C > index e69de29bb2d..7d1748c0f5e 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit2.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit2.C > @@ -0,0 +1,25 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +int foo() { return 42; } > +int g; > + > +struct S { > + explicit(foo()) S(int); // { dg-error "call to" } > + explicit(int) S(int, int); // { dg-error "expected" } > + explicit(false ? 1 : throw 1) S(int, int, int); // { dg-error "not a > constant" } > +}; > + > +struct S2 { > + explicit(true) S2(); > + explicit(false) S2(); // { dg-error "cannot be overloaded" } > +}; > + > +int > +main () > +{ > + S s1 = { 1 }; > + S s2 = { 1, 2 }; // { dg-error "could not convert" } > + S s3 = { 1, 2, 3 }; > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit3.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit3.C > index e69de29bb2d..7c495a3640b 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit3.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit3.C > @@ -0,0 +1,24 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +#include <type_traits> > + > +template <typename T1, typename T2> > +struct pair { > + template <typename U1=T1, typename U2=T2, > + std::enable_if_t< > + std::is_constructible_v<T1, U1> && > + std::is_constructible_v<T2, U2> > + , int> = 0> > + explicit(!std::is_convertible_v<U1, T1> || > + !std::is_convertible_v<U2, T2>) > + constexpr pair(U1&&, U2&&) { } > +}; > + > +void > +foo () > +{ > + pair<int, int> p{1, 2}; > + pair<int, int> p2 = {1, 2}; > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit4.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit4.C > index e69de29bb2d..822a1f155b4 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit4.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit4.C > @@ -0,0 +1,41 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +template<int T = 1> > +struct S { > + explicit(T) S(int); > + explicit(!T) S(int, int); > +}; > + > +template<typename T, int N> > +struct S2 { > + explicit(N) S2(T); > +}; > + > +template<typename T> > +struct S3 { > + explicit((T) 1.0) S3(int); > +}; > + > +int > +main () > +{ > + S<> s1 = { 1 }; // { dg-error "converting" } > + S<true> s2 = { 1 }; // { dg-error "converting" } > + S<false> s3 = { 1 }; > + S<> s4{ 1 }; > + S<true> s5{ 1 }; > + S<> s6 = { 1, 2 }; > + S<true> s7 = { 1, 2 }; > + S<false> s8 = { 1, 2 }; // { dg-error "converting" } > + S<false> s9{ 1, 2 }; > + > + const int x = 1; > + S<x> s10 = { 1 }; // { dg-error "converting" } > + S<x> s11{ 2 }; > + > + S2<int, true> s12 = { 1 }; // { dg-error "converting" } > + > + S3<int> s13 = { 1 }; // { dg-error "converting" } > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit5.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit5.C > index e69de29bb2d..70a106f1fcb 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit5.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit5.C > @@ -0,0 +1,71 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +constexpr int fn0 () { return 0; } > +constexpr int fn1 () { return 1; } > + > +struct S0 { > + explicit(false) operator int(); > + explicit(1 == 0) operator double(); > + explicit(fn0()) operator char(); > +}; > + > +struct S1 { > + explicit(true) operator int(); > + explicit(1 == 1) operator double(); > + explicit(fn1()) operator char(); > +}; > + > +struct X { > + static const bool value = true; > + static constexpr bool foo () { return 1; } > +}; > + > +struct T { > + explicit(true ? 1 : throw 1) operator int(); > + explicit(true || true ? 1 : throw 1) operator double(); > + explicit(X::value) operator char(); > + explicit(X::foo ()) operator long(); > +}; > + > +struct W { > + constexpr operator bool() { return true; }; > +}; > + > +struct W2 { > + constexpr operator bool() { return false; }; > +}; > + > +struct U1 { > + explicit(W()) operator int(); > +}; > + > +struct U2 { > + explicit(W2()) operator int(); > +}; > + > +int > +main () > +{ > + S0 s0; > + S1 s1; > + int i0 = s0; > + int i1 = s1; // { dg-error "cannot convert" } > + double d0 = s0; > + double d1 = s1; // { dg-error "cannot convert" } > + char c0 = s0; > + char c1 = s1; // { dg-error "cannot convert" } > + > + T t; > + int i2 = t; // { dg-error "cannot convert" } > + double d2 = t; // { dg-error "cannot convert" } > + char c2 = t; // { dg-error "cannot convert" } > + long l1 = t; // { dg-error "cannot convert" } > + > + U1 u1; > + int i3 = u1; // { dg-error "cannot convert" } > + > + U2 u2; > + int i4 = u2; > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit6.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit6.C > index e69de29bb2d..10134680ed3 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit6.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit6.C > @@ -0,0 +1,41 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +template<int T = 1> > +struct S { > + explicit(T) operator int(); > +}; > + > +template<typename T, int N> > +struct R { > + explicit(N) operator T(); > +}; > + > +template<typename T> > +struct U { > + explicit((T) 1.0) operator T(); > +}; > + > +int > +main () > +{ > + S s; > + int i1 = s; // { dg-error "cannot convert" } > + S<true> s2; > + int i2 = s2; // { dg-error "cannot convert" } > + S<false> s3; > + int i3 = s3; > + int i4{s}; > + int i5{s2}; > + int i6{s3}; > + > + R<int, true> r; > + int i7 = r; // { dg-error "cannot convert" } > + R<int, false> r2; > + int i8 = r2; > + > + U<int> u; > + int i9 = u; // { dg-error "cannot convert" } > + int i10{u}; > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit7.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit7.C > index e69de29bb2d..dfa4e138d4c 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit7.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit7.C > @@ -0,0 +1,22 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +template<typename T> > +struct B { > + static const T value = true; > +}; > + > +struct X { > + template<typename T> > + explicit(B<T>::value) operator T(); > +}; > + > +int > +main () > +{ > + X x; > + int i = x.operator int(); > + int i3 = x; // { dg-error "cannot convert" } > + int i2{x}; > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit8.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit8.C > index e69de29bb2d..bf2f9ed9917 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit8.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit8.C > @@ -0,0 +1,24 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a" } > + > +struct X { > + template<typename T, int N = 1> > + explicit(N) operator T(); > +}; > + > +int > +main () > +{ > + X x; > + int i = x; // { dg-error "cannot convert" } > + int i2{x}; > + double d = x; // { dg-error "cannot convert" } > + double d2{x}; > + char c = x; // { dg-error "cannot convert" } > + char c2{x}; > + long l = x; // { dg-error "cannot convert" } > + long l2{x}; > + int *p = x; // { dg-error "cannot convert" } > + int *p2{x}; > +} > diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit9.C > gcc/gcc/testsuite/g++.dg/cpp2a/explicit9.C > index e69de29bb2d..6568e5c6661 100644 > --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit9.C > +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit9.C > @@ -0,0 +1,22 @@ > +// P0892R2 > +// { dg-do compile } > +// { dg-options "-std=c++2a -fconcepts" } > + > +#include <type_traits> > + > +template <typename T1, typename T2> > +struct pair { > + template <typename U1=T1, typename U2=T2> > + requires std::is_constructible_v<T1, U1> && > + std::is_constructible_v<T2, U2> > + explicit(!std::is_convertible_v<U1, T1> || > + !std::is_convertible_v<U2, T2>) > + constexpr pair(U1&&, U2&&) { } > +}; > + > +void > +foo () > +{ > + pair<int, int> p{1, 2}; > + pair<int, int> p2 = {1, 2}; > +} > diff --git gcc/libstdc++-v3/testsuite/20_util/any/cons/explicit.cc > gcc/libstdc++-v3/testsuite/20_util/any/cons/explicit.cc > index f5425e09fba..3c13a86a2c9 100644 > --- gcc/libstdc++-v3/testsuite/20_util/any/cons/explicit.cc > +++ gcc/libstdc++-v3/testsuite/20_util/any/cons/explicit.cc > @@ -24,7 +24,7 @@ > > int main() > { > - std::any a = {std::in_place_type<int>, 42}; // { dg-error "converting" } > + std::any a = {std::in_place_type<int>, 42}; // { dg-error "convert" } > std::any a2 = {std::in_place_type<std::vector<int>>, > - {42, 666}}; // { dg-error "converting" } > + {42, 666}}; // { dg-error "convert" } > } > diff --git gcc/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc > gcc/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc > index 67603fb706f..6185ed6cdbc 100644 > --- gcc/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc > +++ gcc/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc > @@ -37,7 +37,7 @@ struct ExplicitDefaultDefault > > std::pair<int, int> f1() {return {1,2};} > > -std::pair<Explicit, Explicit> f2() {return {1,2};} // { dg-error "explicit" } > +std::pair<Explicit, Explicit> f2() {return {1,2};} // { dg-error "could not > convert" } > > std::pair<long, long> f3() {return std::pair<int, int>{1,2};} > > @@ -52,7 +52,7 @@ std::pair<int, int> v0{1,2}; > > std::pair<Explicit, Explicit> v1{1,2}; > > -std::pair<Explicit, Explicit> v2 = {1,2}; // { dg-error "explicit" } > +std::pair<Explicit, Explicit> v2 = {1,2}; // { dg-error "could not convert" } > > std::pair<Explicit, Explicit> v3{std::pair<int,int>{1,2}}; > > @@ -83,12 +83,12 @@ void f7(std::pair<long, long>) {} > > std::pair<ExplicitDefault, int> f8() > { > - return {}; // { dg-error "explicit" } > + return {}; // { dg-error "could not convert" } > } > > std::pair<ExplicitDefaultDefault, int> f9() > { > - return {}; // { dg-error "explicit" } > + return {}; // { dg-error "could not convert" } > } > > void f10(std::pair<ExplicitDefault, int>) {} > @@ -99,7 +99,7 @@ void test_arg_passing() > { > f6(v0); // { dg-error "could not convert" } > f6(v1); > - f6({1,2}); // { dg-error "explicit" } > + f6({1,2}); // { dg-error "could not convert" } > f6(std::pair<Explicit, Explicit>{}); > f6(std::pair<int, int>{}); // { dg-error "could not convert" } > f7(v0); > @@ -107,8 +107,8 @@ void test_arg_passing() > f7({1,2}); > f7(std::pair<int, int>{}); > f7(std::pair<long, long>{}); > - f10({}); // { dg-error "explicit" } > - f11({}); // { dg-error "explicit" } > + f10({}); // { dg-error "could not convert" } > + f11({}); // { dg-error "could not convert" } > f10(std::pair<ExplicitDefault, int>{}); > f11(std::pair<ExplicitDefaultDefault, int>{}); > } > @@ -130,6 +130,6 @@ std::pair<int*, ExplicitMoveOnly> v14{0, MoveOnly{}}; > std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, 0}; > > std::pair<int*, ExplicitMoveOnly> v16 = > - {0, MoveOnly{}}; // { dg-error "explicit" } > + {0, MoveOnly{}}; // { dg-error "could not convert" } > std::pair<ExplicitMoveOnly, int*> v17 = > - {MoveOnly{}, 0}; // { dg-error "explicit" } > + {MoveOnly{}, 0}; // { dg-error "could not convert" } > diff --git > gcc/libstdc++-v3/testsuite/20_util/tuple/cons/explicit_construct.cc > gcc/libstdc++-v3/testsuite/20_util/tuple/cons/explicit_construct.cc > index 2b1de37bb62..6874be40f71 100644 > --- gcc/libstdc++-v3/testsuite/20_util/tuple/cons/explicit_construct.cc > +++ gcc/libstdc++-v3/testsuite/20_util/tuple/cons/explicit_construct.cc > @@ -43,11 +43,11 @@ std::tuple<int, int> f1b() {return {1,2};} > std::tuple<int, int, int> f1c() {return {1,2,3};} > > std::tuple<Explicit> f2_a() > -{return {1};} // { dg-error "explicit" } > +{return {1};} // { dg-error "could not convert" } > std::tuple<Explicit, Explicit> f2_b() > -{return {1,2};} // { dg-error "explicit" } > +{return {1,2};} // { dg-error "could not convert" } > std::tuple<Explicit, Explicit, Explicit> f2_c() > -{return {1,2,3};} // { dg-error "explicit" } > +{return {1,2,3};} // { dg-error "could not convert" } > > std::tuple<long> f3_a() {return std::tuple<int>{1};} > std::tuple<long, long> f3_b() {return std::tuple<int, int>{1,2};} > @@ -71,22 +71,22 @@ std::tuple<long, long> f5_b() {return {1,2};} > std::tuple<long, long, long> f5_c() {return {1,2,3};} > > std::tuple<ExplicitDefault> f6_a() > -{return {};} // { dg-error "explicit" } > +{return {};} // { dg-error "could not convert" } > std::tuple<ExplicitDefault, ExplicitDefault> f6_b() > -{return {};} // { dg-error "explicit" } > +{return {};} // { dg-error "could not convert" } > std::tuple<ExplicitDefault, ExplicitDefault, ExplicitDefault> f6_c() > -{return {};} // { dg-error "explicit" } > +{return {};} // { dg-error "could not convert" } > std::tuple<ExplicitDefault, int> f6_d() > -{return {};} // { dg-error "explicit" } > +{return {};} // { dg-error "could not convert" } > > std::tuple<ExplicitDefaultDefault> f7_a() > -{return {};} // { dg-error "explicit" } > +{return {};} // { dg-error "could not convert" } > std::tuple<ExplicitDefaultDefault, ExplicitDefaultDefault> f7_b() > -{return {};} // { dg-error "explicit" } > +{return {};} // { dg-error "could not convert" } > std::tuple<ExplicitDefaultDefault, > ExplicitDefaultDefault, > ExplicitDefaultDefault> f7_c() > -{return {};} // { dg-error "explicit" } > +{return {};} // { dg-error "could not convert" } > > std::tuple<int, int> fp1() {return std::pair<int, int>{1,2}; } > std::tuple<long, long> fp2() {return std::pair<int, int>{1,2}; } > @@ -101,9 +101,9 @@ std::tuple<Explicit> v1_a{1}; > std::tuple<Explicit, Explicit> v1_b{1,2}; > std::tuple<Explicit, Explicit, Explicit> v1_c{1,2,3}; > > -std::tuple<Explicit> v2_a = {1}; // { dg-error "explicit" } > -std::tuple<Explicit, Explicit> v2_b = {1,2}; // { dg-error "explicit" } > -std::tuple<Explicit, Explicit, Explicit> v2_c = {1,2,3}; // { dg-error > "explicit" } > +std::tuple<Explicit> v2_a = {1}; // { dg-error "could not convert" } > +std::tuple<Explicit, Explicit> v2_b = {1,2}; // { dg-error "could not > convert" } > +std::tuple<Explicit, Explicit, Explicit> v2_c = {1,2,3}; // { dg-error > "could not convert" } > > std::tuple<Explicit> v3_a{std::tuple<int>{1}}; > std::tuple<Explicit, Explicit> v3_b{std::tuple<int,int>{1,2}}; > @@ -194,11 +194,11 @@ std::tuple<long, long, long> > v31_c{std::allocator_arg, std::allocator<int>{}, 1,2,3}; > > std::tuple<Explicit> v32_a > - = {std::allocator_arg, std::allocator<int>{ }, 1}; // { dg-error > "explicit" } > + = {std::allocator_arg, std::allocator<int>{ }, 1}; // { dg-error "could > not convert" } > std::tuple<Explicit, Explicit> v32_b > - = {std::allocator_arg, std::allocator<int>{}, 1, 2}; // { dg-error > "explicit" } > + = {std::allocator_arg, std::allocator<int>{}, 1, 2}; // { dg-error "could > not convert" } > std::tuple<Explicit, Explicit, Explicit> v32_c > - = {std::allocator_arg, std::allocator<int>{}, 1,2,3}; // { dg-error > "explicit" } > + = {std::allocator_arg, std::allocator<int>{}, 1,2,3}; // { dg-error "could > not convert" } > > std::tuple<int, int> v33{std::allocator_arg, std::allocator<int>{}, > std::pair<int, int>{1, 2}}; > @@ -216,7 +216,7 @@ std::tuple<long, long> v37 = {std::allocator_arg, > std::allocator<int>{}, > std::pair<int, int>{1, 2}}; > > std::tuple<Explicit, Explicit> v38 > -= {std::allocator_arg, std::allocator<int>{}, std::pair<int, int>{1, 2}}; // > { dg-error "explicit" } > += {std::allocator_arg, std::allocator<int>{}, std::pair<int, int>{1, 2}}; // > { dg-error "could not convert" } > > std::tuple<int, int> v39{std::allocator_arg, std::allocator<int>{}, v20}; > > @@ -230,18 +230,18 @@ std::tuple<int, int> v42 = {std::allocator_arg, > std::allocator<int>{}, v20}; > std::tuple<long, long> v43 = {std::allocator_arg, std::allocator<int>{}, > v20}; > > std::tuple<Explicit, Explicit> v44 > -= {std::allocator_arg, std::allocator<int>{ }, v20}; // { dg-error > "explicit" } > += {std::allocator_arg, std::allocator<int>{ }, v20}; // { dg-error "could > not convert" } > std::tuple<ExplicitDefault> v45_a{}; > std::tuple<ExplicitDefault, int> v45_b{}; > > -std::tuple<ExplicitDefault> v46_a = {}; // { dg-error "explicit" } > -std::tuple<ExplicitDefault, int> v46_b = {}; // { dg-error "explicit" } > +std::tuple<ExplicitDefault> v46_a = {}; // { dg-error "could not convert" } > +std::tuple<ExplicitDefault, int> v46_b = {}; // { dg-error "could not > convert" } > > std::tuple<ExplicitDefaultDefault> v47_a{}; > std::tuple<ExplicitDefaultDefault, int> v47_b{}; > > -std::tuple<ExplicitDefaultDefault> v48_a = {}; // { dg-error "explicit" } > -std::tuple<ExplicitDefaultDefault, int> v48_b = { }; // { dg-error > "explicit" } > +std::tuple<ExplicitDefaultDefault> v48_a = {}; // { dg-error "could not > convert" } > +std::tuple<ExplicitDefaultDefault, int> v48_b = { }; // { dg-error "could > not convert" } > > > struct DeletedCopy > @@ -293,9 +293,9 @@ void test_arg_passing() > f8_b(v1_b); > f8_c(v1_c); > > - f8_a({1}); // { dg-error "explicit" } > - f8_b({1,2}); // { dg-error "explicit" } > - f8_c({1,2,3}); // { dg-error "explicit" } > + f8_a({1}); // { dg-error "could not convert" } > + f8_b({1,2}); // { dg-error "could not convert" } > + f8_c({1,2,3}); // { dg-error "could not convert" } > > f8_a(std::tuple<Explicit>{}); > f8_b(std::tuple<Explicit, Explicit>{}); > @@ -328,10 +328,10 @@ void test_arg_passing() > f9_b(std::tuple<long, long>{}); > f9_c(std::tuple<long, long, long>{}); > > - f10_a({}); // { dg-error "explicit" } > - f10_b({}); // { dg-error "explicit" } > - f11_a({}); // { dg-error "explicit" } > - f11_b({}); // { dg-error "explicit" } > + f10_a({}); // { dg-error "could not convert" } > + f10_b({}); // { dg-error "could not convert" } > + f11_a({}); // { dg-error "could not convert" } > + f11_b({}); // { dg-error "could not convert" } > > f10_a(std::tuple<ExplicitDefault>{}); > f10_b(std::tuple<ExplicitDefault, int>{});
Marek