Implement P0892R2, explicit(bool).
authorMarek Polacek <polacek@redhat.com>
Tue, 30 Oct 2018 19:59:41 +0000 (19:59 +0000)
committerMarek Polacek <mpolacek@gcc.gnu.org>
Tue, 30 Oct 2018 19:59:41 +0000 (19:59 +0000)
* 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.

From-SVN: r265641

26 files changed:
gcc/c-family/ChangeLog
gcc/c-family/c-cppbuiltin.c
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp2a/explicit1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit13.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/explicit9.C [new file with mode: 0644]
libstdc++-v3/ChangeLog
libstdc++-v3/testsuite/20_util/any/cons/explicit.cc
libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
libstdc++-v3/testsuite/20_util/tuple/cons/explicit_construct.cc

index 64e098816ec4f364c6fb962a7ba2cb25e84bab70..ace36fb30e8d2116763c96528dd3bc10a5e58c1d 100644 (file)
@@ -1,3 +1,8 @@
+2018-10-30  Marek Polacek  <polacek@redhat.com>
+
+       Implement P0892R2, explicit(bool).
+       * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_explicit_bool.
+
 2018-10-29  David Malcolm  <dmalcolm@redhat.com>
 
        * name-hint.h (name_hint::take_deferred): New member function.
index 96a6b4dfd2b9e898567aeb9e9586e5738fdeb15d..b085cf9201fdf01c20185ea262b597cbca6306b7 100644 (file)
@@ -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)
index 75fcc8adc58d46168274acad0788bc671e4a2d86..d76d3177a1c5ffa6a9a3fed04069ac0602fe1dbf 100644 (file)
@@ -1,3 +1,19 @@
+2018-10-30  Marek Polacek  <polacek@redhat.com>
+
+       Implement P0892R2, 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).
+
 2018-10-30  Paolo Carlini  <paolo.carlini@oracle.com>
 
        * decl.c (grokdeclarator): Use declarator->id_loc in diagnostic
index a7dce2ea0aadce80ab9953dd43adc7c7dc256e73..6f401567c2e198674204e0d265ed67a57f3e45f2 100644 (file)
@@ -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);
index 8454cb4e178528f377692554a82e820ff8a39d49..c9427a0b624d921ed3c30cb71c991c87aa5dbf78 100644 (file)
@@ -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);
@@ -6772,6 +6782,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);
index 496ed98d9a5a495e07841bd4acf1696db0202ca4..11320b65e715ffcc1fe15c4f888c09bf2b122e52 100644 (file)
@@ -12382,6 +12382,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))
@@ -16562,4 +16565,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"
index c4bda7fc59e7cb236f1694b00c82b0849be5862b..206ceb048d41e3e5f0ce9dc1f2bb072c4c0a1fe4 100644 (file)
@@ -13897,6 +13897,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.  */
 
@@ -13923,8 +13926,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;
index f290cb32fc28b8257de010c77426b85628bbb86b..fc6cf98950195af21b008ab8c654dcb79be5bd48 100644 (file)
@@ -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
index 491e58158663d4f7b47b198752c99f18bd59aa27..cab592771963b921f41cace0983e141333356426 100644 (file)
@@ -1,3 +1,20 @@
+2018-10-30  Marek Polacek  <polacek@redhat.com>
+
+       Implement P0892R2, explicit(bool).
+       * 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.
+
 2018-10-30  Segher Boessenkool  <segher@kernel.crashing.org>
 
        PR rtl-optimization/87708
diff --git a/gcc/testsuite/g++.dg/cpp2a/explicit1.C b/gcc/testsuite/g++.dg/cpp2a/explicit1.C
new file mode 100644 (file)
index 0000000..b39f90f
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit10.C b/gcc/testsuite/g++.dg/cpp2a/explicit10.C
new file mode 100644 (file)
index 0000000..c870155
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit11.C b/gcc/testsuite/g++.dg/cpp2a/explicit11.C
new file mode 100644 (file)
index 0000000..ad1bed5
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit12.C b/gcc/testsuite/g++.dg/cpp2a/explicit12.C
new file mode 100644 (file)
index 0000000..6db3157
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit13.C b/gcc/testsuite/g++.dg/cpp2a/explicit13.C
new file mode 100644 (file)
index 0000000..4747ebd
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit2.C b/gcc/testsuite/g++.dg/cpp2a/explicit2.C
new file mode 100644 (file)
index 0000000..7d1748c
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit3.C b/gcc/testsuite/g++.dg/cpp2a/explicit3.C
new file mode 100644 (file)
index 0000000..7c495a3
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit4.C b/gcc/testsuite/g++.dg/cpp2a/explicit4.C
new file mode 100644 (file)
index 0000000..822a1f1
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit5.C b/gcc/testsuite/g++.dg/cpp2a/explicit5.C
new file mode 100644 (file)
index 0000000..70a106f
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit6.C b/gcc/testsuite/g++.dg/cpp2a/explicit6.C
new file mode 100644 (file)
index 0000000..1013468
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit7.C b/gcc/testsuite/g++.dg/cpp2a/explicit7.C
new file mode 100644 (file)
index 0000000..dfa4e13
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit8.C b/gcc/testsuite/g++.dg/cpp2a/explicit8.C
new file mode 100644 (file)
index 0000000..bf2f9ed
--- /dev/null
@@ -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 a/gcc/testsuite/g++.dg/cpp2a/explicit9.C b/gcc/testsuite/g++.dg/cpp2a/explicit9.C
new file mode 100644 (file)
index 0000000..6568e5c
--- /dev/null
@@ -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};
+}
index 5dc4295a1f697f563d101c17af58ff65b47cd3e9..d9d4f0e64e0fbf557968c080aef302010fa874f1 100644 (file)
@@ -1,3 +1,10 @@
+2018-10-30  Marek Polacek  <polacek@redhat.com>
+
+       Implement P0892R2, explicit(bool).
+       * 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.
+
 2018-10-30  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/87809
index f5425e09fbad3d9bd91a8bfadda4305acfcedf49..3c13a86a2c9a838eceaa561c18d4c4c1d31ea6cc 100644 (file)
@@ -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" }
 }
index 67603fb706f9a9ba22c7462cdc0dda5a47acaeee..6185ed6cdbcea69b1ec4b8c4c7865fda061af5f6 100644 (file)
@@ -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" }
index 2b1de37bb627d31d0c3edd1a2d84edde0c929342..6874be40f71c59fb22e54beebcfa93bceee403ba 100644 (file)
@@ -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>{});