aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2018-10-30 19:59:41 +0000
committerMarek Polacek <mpolacek@gcc.gnu.org>2018-10-30 19:59:41 +0000
commitb5ff4f5c0d61e52e27a0727ae9e011aab525ccfd (patch)
treec6e4a49a90145c7f755d5dde23b9b457c4d25a0b /gcc
parent8d42623b370f4b3f11074ae783709c2b8a7fb65a (diff)
downloadgcc-b5ff4f5c0d61e52e27a0727ae9e011aab525ccfd.zip
gcc-b5ff4f5c0d61e52e27a0727ae9e011aab525ccfd.tar.gz
gcc-b5ff4f5c0d61e52e27a0727ae9e011aab525ccfd.tar.bz2
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. From-SVN: r265641
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/ChangeLog5
-rw-r--r--gcc/c-family/c-cppbuiltin.c7
-rw-r--r--gcc/cp/ChangeLog16
-rw-r--r--gcc/cp/call.c6
-rw-r--r--gcc/cp/cp-tree.h13
-rw-r--r--gcc/cp/decl.c19
-rw-r--r--gcc/cp/parser.c52
-rw-r--r--gcc/cp/pt.c33
-rw-r--r--gcc/testsuite/ChangeLog17
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit1.C63
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit10.C32
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit11.C29
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit12.C23
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit13.C35
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit2.C25
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit3.C24
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit4.C41
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit5.C71
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit6.C41
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit7.C22
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit8.C24
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit9.C22
22 files changed, 616 insertions, 4 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 64e0988..ace36fb 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -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.
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 96a6b4d..b085cf92 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/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 a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 75fcc8a..d76d317 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index a7dce2e..6f40156 100644
--- a/gcc/cp/call.c
+++ b/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 a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8454cb4..c9427a0 100644
--- a/gcc/cp/cp-tree.h
+++ b/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);
@@ -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);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 496ed98..11320b6 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -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"
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c4bda7f..206ceb0 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index f290cb3..fc6cf9895 100644
--- a/gcc/cp/pt.c
+++ b/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 a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 491e581..cab5927 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..b39f90f
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit10.C b/gcc/testsuite/g++.dg/cpp2a/explicit10.C
new file mode 100644
index 0000000..c870155
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit11.C b/gcc/testsuite/g++.dg/cpp2a/explicit11.C
new file mode 100644
index 0000000..ad1bed5
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit12.C b/gcc/testsuite/g++.dg/cpp2a/explicit12.C
new file mode 100644
index 0000000..6db3157
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit13.C b/gcc/testsuite/g++.dg/cpp2a/explicit13.C
new file mode 100644
index 0000000..4747ebd
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit2.C b/gcc/testsuite/g++.dg/cpp2a/explicit2.C
new file mode 100644
index 0000000..7d1748c
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit3.C b/gcc/testsuite/g++.dg/cpp2a/explicit3.C
new file mode 100644
index 0000000..7c495a3
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit4.C b/gcc/testsuite/g++.dg/cpp2a/explicit4.C
new file mode 100644
index 0000000..822a1f1
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit5.C b/gcc/testsuite/g++.dg/cpp2a/explicit5.C
new file mode 100644
index 0000000..70a106f
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit6.C b/gcc/testsuite/g++.dg/cpp2a/explicit6.C
new file mode 100644
index 0000000..1013468
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit7.C b/gcc/testsuite/g++.dg/cpp2a/explicit7.C
new file mode 100644
index 0000000..dfa4e13
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit8.C b/gcc/testsuite/g++.dg/cpp2a/explicit8.C
new file mode 100644
index 0000000..bf2f9ed
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/explicit9.C b/gcc/testsuite/g++.dg/cpp2a/explicit9.C
new file mode 100644
index 0000000..6568e5c
--- /dev/null
+++ b/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};
+}