On Wed, Oct 24, 2018 at 02:55:14PM -0400, Jason Merrill wrote: > On 10/12/18 12:32 PM, Marek Polacek wrote: > > + 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) > > Why not add the explicit-specifier to cp_decl_specifier_seq? They don't > live very long, so making them bigger isn't a concern. Then other of the > handling could move into grokdeclarator along with the other explicit > handling.
Great -- that simplifies things. > > @@ -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); > > + } > > This is setting DECL_NONCONVERTING_P on the template, rather than the > instantiation r, which hasn't been created yet at this point; this handling > needs to move further down in the function. Hmm, interesting that that worked, too. Anyway, fixed. Thanks! Bootstrapped/regtested on x86_64-linux, ok for trunk? 2018-10-29 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. (cp_decl_specifier_seq): Add explicit_specifier field. (build_explicit_specifier, store_explicit_specifier): Declare. * decl.c (grokdeclarator): Call store_explicit_specifier. (build_explicit_specifier): New function. * parser.c (cp_parser_function_specifier_opt) <case RID_EXPLICIT>: Parse C++20 explicit(bool). * 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/explicit12.C: New test. * g++.dg/cpp2a/explicit13.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 cd0c0f60ced..e3453aca96e 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 26ded3a9214..9893ad950f5 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 @@ -5748,6 +5755,8 @@ struct cp_decl_specifier_seq { /* If non-NULL, a built-in type that the user attempted to redefine to some other type. */ tree redefined_builtin_type; + /* The explicit-specifier, if any. */ + tree explicit_specifier; /* The storage class specified -- or sc_none if no storage class was explicitly specified. */ cp_storage_class storage_class; @@ -6375,6 +6384,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); @@ -6771,6 +6781,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 5ebfaaf85e6..9e089216674 100644 --- gcc/gcc/cp/decl.c +++ gcc/gcc/cp/decl.c @@ -12380,6 +12380,9 @@ grokdeclarator (const cp_declarator *declarator, is called a converting constructor. */ if (explicitp == 2) DECL_NONCONVERTING_P (decl) = 1; + + if (declspecs->explicit_specifier) + store_explicit_specifier (decl, declspecs->explicit_specifier); } else if (!staticp && !dependent_type_p (type) && !COMPLETE_TYPE_P (complete_type (type)) @@ -16560,4 +16563,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 ebe326eb923..52e9fcab458 100644 --- gcc/gcc/cp/parser.c +++ gcc/gcc/cp/parser.c @@ -13867,6 +13867,9 @@ 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. */ @@ -13893,8 +13896,53 @@ 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 (expr == boolean_false_node) + /* Don't mark the decl as explicit. */; + else if (decl_specs) + /* The expression was value-dependent. Remember it so that we can + substitute it later. */ + decl_specs->explicit_specifier = expr; + return id; + } default: return NULL_TREE; diff --git gcc/gcc/cp/pt.c gcc/gcc/cp/pt.c index f290cb32fc2..fc6cf989501 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 @@ -12943,6 +12965,17 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, DECL_INITIAL (r) = NULL_TREE; DECL_CONTEXT (r) = ctx; + /* 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 (r) = (spec == boolean_true_node); + } + /* OpenMP UDRs have the only argument a reference to the declared type. We want to diagnose if the declared type is a reference, which is invalid, but as references to references are usually 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/explicit12.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit12.C index e69de29bb2d..6db3157580b 100644 --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit12.C +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit12.C @@ -0,0 +1,23 @@ +// P0892R2 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template<typename> struct A { + template<typename T, int N = 0> + explicit(N) operator T(); +}; + +template<typename> struct B { + template<typename T, int N = 1> + explicit(N) operator T(); +}; + +void +bar () +{ + A<int> a; + int i = a; + + B<int> b; + int j = b; // { dg-error "cannot convert" } +} diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit13.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit13.C index e69de29bb2d..4747ebd3df4 100644 --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit13.C +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit13.C @@ -0,0 +1,35 @@ +// P0892R2 +// { dg-do compile } +// { dg-options "-std=c++2a" } + +template<int M = 0> struct A { + template<typename T, int N = 0> + explicit(N + M) operator T(); +}; + +template<int M = 1> struct B { + template<typename T, int N = 1> + explicit(N * M) operator T(); +}; + +void +bar () +{ + A a; + int i = a; + + A<0> a0; + int i0 = a0; + + A<1> a1; + int i1 = a1; // { dg-error "cannot convert" } + + B b; + int j = b; // { dg-error "cannot convert" } + + B<0> b0; + int j0 = b0; + + B<1> b1; + int j1 = b1; // { dg-error "cannot convert" } +} 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>{});