diff mbox series

C++ PATCH to implement C++20 P0892R2 - explicit(bool)

Message ID 20181001224137.GN5587@redhat.com
State New
Headers show
Series C++ PATCH to implement C++20 P0892R2 - explicit(bool) | expand

Commit Message

Marek Polacek Oct. 1, 2018, 10:41 p.m. UTC
This patch implements C++20 explicit(bool), as described in:
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.

I tried to follow the noexcept specifier implementation where I could, which
made the non-template parts of this fairly easy.  To make explicit(expr) work
with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
which serves as a vessel to get the explicit-specifier to tsubst_function_decl
where I substitute the dependent arguments.

I've written a bunch of tests testing the basic functionality but I don't
doubt I've missed a couple of important scenarios.  I hope we can shake
those out as people start playing with this feature.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2018-10-01  Marek Polacek  <polacek@redhat.com>

	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 explicit_specifier field.
	(DECL_EXPLICIT_SPEC): New macro.
	(build_explicit_specifier): Declare.
	* decl.c (build_explicit_specifier): New function.
	* parser.c (cp_parser_decl_specifier_seq): Add explicit_specifier
	parameter.  Pass it down to cp_parser_function_specifier_opt.
	(cp_parser_function_specifier_opt): Add explicit_specifier parameter.
	<case RID_EXPLICIT>: Parse C++20 explicit(bool).
	(cp_parser_explicit_instantiation): Update call to
	cp_parser_function_specifier_opt.
	(cp_parser_member_declaration): Have cp_parser_decl_specifier_seq save
	the explicit-specifier.  Save it to DECL_EXPLICIT_SPEC.
	(cp_parser_single_declaration): Likewise.
	* pt.c (tsubst_function_decl): Handle explicit(dependent-expr).

	* g++.dg/cpp2a/explicit1.C: New test.
	* g++.dg/cpp2a/explicit10.C: New test.
	* g++.dg/cpp2a/explicit11.C: New test.
	* g++.dg/cpp2a/explicit2.C: New test.
	* g++.dg/cpp2a/explicit3.C: New test.
	* g++.dg/cpp2a/explicit4.C: New test.
	* g++.dg/cpp2a/explicit5.C: New test.
	* g++.dg/cpp2a/explicit6.C: New test.
	* g++.dg/cpp2a/explicit7.C: New test.
	* g++.dg/cpp2a/explicit8.C: New test.
	* g++.dg/cpp2a/explicit9.C: New test.

	* testsuite/20_util/any/cons/explicit.cc: Adjust dg-error.
	* testsuite/20_util/pair/cons/explicit_construct.cc: Likewise.
	* testsuite/20_util/tuple/cons/explicit_construct.cc: Likewise.

Comments

Jason Merrill Oct. 1, 2018, 11:47 p.m. UTC | #1
On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
>
> This patch implements C++20 explicit(bool), as described in:
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
>
> I tried to follow the noexcept specifier implementation where I could, which
> made the non-template parts of this fairly easy.  To make explicit(expr) work
> with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
> which serves as a vessel to get the explicit-specifier to tsubst_function_decl
> where I substitute the dependent arguments.

What's the impact of that on memory consumption?  I'm nervous about
adding another word to most functions when it's not useful to most of
them.  For several similar things we've been using hash tables on the
side.

> +/* Create a representation of the explicit-specifier with
> +   constant-expression of EXPR.  COMPLAIN is as for tsubst.  */
> +
> +tree
> +build_explicit_specifier (tree expr, tsubst_flags_t complain)
> +{
> +  if (processing_template_decl && value_dependent_expression_p (expr))
> +    /* Wait for instantiation.  tsubst_function_decl will take care of it.  */
> +    return expr;
> +
> +  expr = perform_implicit_conversion_flags (boolean_type_node, expr,
> +                                           complain, LOOKUP_NORMAL);
> +  expr = instantiate_non_dependent_expr (expr);
> +  expr = cxx_constant_value (expr);
> +  return expr;
> +}

Is there a reason not to use build_converted_constant_expr?

Jason
Marek Polacek Oct. 2, 2018, 9:25 p.m. UTC | #2
On Mon, Oct 01, 2018 at 07:47:10PM -0400, Jason Merrill wrote:
> On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
> >
> > This patch implements C++20 explicit(bool), as described in:
> > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
> >
> > I tried to follow the noexcept specifier implementation where I could, which
> > made the non-template parts of this fairly easy.  To make explicit(expr) work
> > with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
> > which serves as a vessel to get the explicit-specifier to tsubst_function_decl
> > where I substitute the dependent arguments.
> 
> What's the impact of that on memory consumption?  I'm nervous about
> adding another word to most functions when it's not useful to most of
> them.  For several similar things we've been using hash tables on the
> side.

Yeah, that is a fair concern.  I'm not sure if I know of a good way to measure
it.  I took wide-int.ii and ran /usr/bin/time -v ./cc1plus; then it's roughly
Maximum resident set size (kbytes): 95020
vs.				    
Maximum resident set size (kbytes): 95272
which doesn't seem too bad but I don't know if it proves anything.

If we went with the hash table, would it work like this?
1) have a hash table mapping decls (key) to explicit-specifiers
2) instead of setting DECL_EXPLICIT_SPEC put the parsed explicit-specifier
   into the table
3) in tsubst_function_decl look if the fn decl is associated with any
   explicit-specifier, if it is, substitute it, and set DECL_NONCONVERTING_P
   accordingly.

> > +/* Create a representation of the explicit-specifier with
> > +   constant-expression of EXPR.  COMPLAIN is as for tsubst.  */
> > +
> > +tree
> > +build_explicit_specifier (tree expr, tsubst_flags_t complain)
> > +{
> > +  if (processing_template_decl && value_dependent_expression_p (expr))
> > +    /* Wait for instantiation.  tsubst_function_decl will take care of it.  */
> > +    return expr;
> > +
> > +  expr = perform_implicit_conversion_flags (boolean_type_node, expr,
> > +                                           complain, LOOKUP_NORMAL);
> > +  expr = instantiate_non_dependent_expr (expr);
> > +  expr = cxx_constant_value (expr);
> > +  return expr;
> > +}
> 
> Is there a reason not to use build_converted_constant_expr?

build_converted_constant_expr doesn't allow narrowing conversions but we should
allow them in explicit-specifier which takes "contextually converted constant
expression of type bool", much like in

constexpr int foo () { return 42; }
static_assert (foo());

Right?

Marek
Jason Merrill Oct. 3, 2018, 2:24 p.m. UTC | #3
On Tue, Oct 2, 2018 at 5:25 PM Marek Polacek <polacek@redhat.com> wrote:
>
> On Mon, Oct 01, 2018 at 07:47:10PM -0400, Jason Merrill wrote:
> > On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
> > >
> > > This patch implements C++20 explicit(bool), as described in:
> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
> > >
> > > I tried to follow the noexcept specifier implementation where I could, which
> > > made the non-template parts of this fairly easy.  To make explicit(expr) work
> > > with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
> > > which serves as a vessel to get the explicit-specifier to tsubst_function_decl
> > > where I substitute the dependent arguments.
> >
> > What's the impact of that on memory consumption?  I'm nervous about
> > adding another word to most functions when it's not useful to most of
> > them.  For several similar things we've been using hash tables on the
> > side.
>
> Yeah, that is a fair concern.  I'm not sure if I know of a good way to measure
> it.  I took wide-int.ii and ran /usr/bin/time -v ./cc1plus; then it's roughly
> Maximum resident set size (kbytes): 95020
> vs.
> Maximum resident set size (kbytes): 95272
> which doesn't seem too bad but I don't know if it proves anything.
>
> If we went with the hash table, would it work like this?
> 1) have a hash table mapping decls (key) to explicit-specifiers
> 2) instead of setting DECL_EXPLICIT_SPEC put the parsed explicit-specifier
>    into the table
> 3) in tsubst_function_decl look if the fn decl is associated with any
>    explicit-specifier, if it is, substitute it, and set DECL_NONCONVERTING_P
>    accordingly.

Yes.  I think you want to use tree_cache_map so you don't have to
worry about removing entries from the table if the decl is GC'd.

> > > +/* Create a representation of the explicit-specifier with
> > > +   constant-expression of EXPR.  COMPLAIN is as for tsubst.  */
> > > +
> > > +tree
> > > +build_explicit_specifier (tree expr, tsubst_flags_t complain)
> > > +{
> > > +  if (processing_template_decl && value_dependent_expression_p (expr))
> > > +    /* Wait for instantiation.  tsubst_function_decl will take care of it.  */
> > > +    return expr;
> > > +
> > > +  expr = perform_implicit_conversion_flags (boolean_type_node, expr,
> > > +                                           complain, LOOKUP_NORMAL);
> > > +  expr = instantiate_non_dependent_expr (expr);
> > > +  expr = cxx_constant_value (expr);
> > > +  return expr;
> > > +}
> >
> > Is there a reason not to use build_converted_constant_expr?
>
> build_converted_constant_expr doesn't allow narrowing conversions but we should
> allow them in explicit-specifier which takes "contextually converted constant
> expression of type bool", much like in
>
> constexpr int foo () { return 42; }
> static_assert (foo());
>
> Right?

That's what the standard seems to require, but I think that's broken;
I cc'd you on my email to the reflector.

Jason
Nathan Sidwell Oct. 3, 2018, 4:30 p.m. UTC | #4
On 10/1/18 7:47 PM, Jason Merrill wrote:
> On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
>>
>> This patch implements C++20 explicit(bool), as described in:
>> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
>>
>> I tried to follow the noexcept specifier implementation where I could, which
>> made the non-template parts of this fairly easy.  To make explicit(expr) work
>> with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
>> which serves as a vessel to get the explicit-specifier to tsubst_function_decl
>> where I substitute the dependent arguments.
> 
> What's the impact of that on memory consumption?  I'm nervous about
> adding another word to most functions when it's not useful to most of
> them.  For several similar things we've been using hash tables on the
> side.

Iain had the same question about coroutine state.  For the moment we're 
doing the simple thing of growing lang_decl_fn.  It seems the better 
approach is a flag in the function_decl (or it's decl_lang_specific 
extension) to say 'go look over there', and a hash table you know will 
contain something.

nathan
Nathan Sidwell Oct. 3, 2018, 4:35 p.m. UTC | #5
On 10/2/18 5:25 PM, Marek Polacek wrote:
> On Mon, Oct 01, 2018 at 07:47:10PM -0400, Jason Merrill wrote:
>> On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
>>>
>>> This patch implements C++20 explicit(bool), as described in:
>>> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
>>>
>>> I tried to follow the noexcept specifier implementation where I could, which
>>> made the non-template parts of this fairly easy.  To make explicit(expr) work
>>> with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
>>> which serves as a vessel to get the explicit-specifier to tsubst_function_decl
>>> where I substitute the dependent arguments.
>>
>> What's the impact of that on memory consumption?  I'm nervous about
>> adding another word to most functions when it's not useful to most of
>> them.  For several similar things we've been using hash tables on the
>> side.
> 
> Yeah, that is a fair concern.  I'm not sure if I know of a good way to measure
> it.  I took wide-int.ii and ran /usr/bin/time -v ./cc1plus; then it's roughly
> Maximum resident set size (kbytes): 95020
> vs.				
> Maximum resident set size (kbytes): 95272
> which doesn't seem too bad but I don't know if it proves anything.
> 
> If we went with the hash table, would it work like this?
> 1) have a hash table mapping decls (key) to explicit-specifiers
> 2) instead of setting DECL_EXPLICIT_SPEC put the parsed explicit-specifier
>     into the table
> 3) in tsubst_function_decl look if the fn decl is associated with any
>     explicit-specifier, if it is, substitute it, and set DECL_NONCONVERTING_P
>     accordingly.

Sounds right, maybe with the flag idea I mentioned in the other email to 
say it's worth looking in the hash table.  Hashing can be by tree 
address of course.

natha
Jakub Jelinek Oct. 3, 2018, 5:44 p.m. UTC | #6
On Wed, Oct 03, 2018 at 12:30:28PM -0400, Nathan Sidwell wrote:
> On 10/1/18 7:47 PM, Jason Merrill wrote:
> > On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
> > > 
> > > This patch implements C++20 explicit(bool), as described in:
> > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
> > > 
> > > I tried to follow the noexcept specifier implementation where I could, which
> > > made the non-template parts of this fairly easy.  To make explicit(expr) work
> > > with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
> > > which serves as a vessel to get the explicit-specifier to tsubst_function_decl
> > > where I substitute the dependent arguments.
> > 
> > What's the impact of that on memory consumption?  I'm nervous about
> > adding another word to most functions when it's not useful to most of
> > them.  For several similar things we've been using hash tables on the
> > side.
> 
> Iain had the same question about coroutine state.  For the moment we're
> doing the simple thing of growing lang_decl_fn.  It seems the better
> approach is a flag in the function_decl (or it's decl_lang_specific
> extension) to say 'go look over there', and a hash table you know will
> contain something.

Yeah, that is what is used e.g. for DECL_VALUE_EXPR, DECL_DEBUG_EXPR and
many other things.

	Jakub
Marek Polacek Oct. 3, 2018, 6:09 p.m. UTC | #7
On Wed, Oct 03, 2018 at 07:44:03PM +0200, Jakub Jelinek wrote:
> On Wed, Oct 03, 2018 at 12:30:28PM -0400, Nathan Sidwell wrote:
> > On 10/1/18 7:47 PM, Jason Merrill wrote:
> > > On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
> > > > 
> > > > This patch implements C++20 explicit(bool), as described in:
> > > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
> > > > 
> > > > I tried to follow the noexcept specifier implementation where I could, which
> > > > made the non-template parts of this fairly easy.  To make explicit(expr) work
> > > > with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
> > > > which serves as a vessel to get the explicit-specifier to tsubst_function_decl
> > > > where I substitute the dependent arguments.
> > > 
> > > What's the impact of that on memory consumption?  I'm nervous about
> > > adding another word to most functions when it's not useful to most of
> > > them.  For several similar things we've been using hash tables on the
> > > side.
> > 
> > Iain had the same question about coroutine state.  For the moment we're
> > doing the simple thing of growing lang_decl_fn.  It seems the better
> > approach is a flag in the function_decl (or it's decl_lang_specific
> > extension) to say 'go look over there', and a hash table you know will
> > contain something.
> 
> Yeah, that is what is used e.g. for DECL_VALUE_EXPR, DECL_DEBUG_EXPR and
> many other things.

Thanks.  We have 13 spare bits in lang_decl_fn so taking one of them should
be fair game.

I'll rework my patch as per above.

Marek
Marek Polacek Oct. 3, 2018, 11:11 p.m. UTC | #8
On Wed, Oct 03, 2018 at 10:24:52AM -0400, Jason Merrill wrote:
> On Tue, Oct 2, 2018 at 5:25 PM Marek Polacek <polacek@redhat.com> wrote:
> >
> > On Mon, Oct 01, 2018 at 07:47:10PM -0400, Jason Merrill wrote:
> > > On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
> > > >
> > > > This patch implements C++20 explicit(bool), as described in:
> > > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
> > > >
> > > > I tried to follow the noexcept specifier implementation where I could, which
> > > > made the non-template parts of this fairly easy.  To make explicit(expr) work
> > > > with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
> > > > which serves as a vessel to get the explicit-specifier to tsubst_function_decl
> > > > where I substitute the dependent arguments.
> > >
> > > What's the impact of that on memory consumption?  I'm nervous about
> > > adding another word to most functions when it's not useful to most of
> > > them.  For several similar things we've been using hash tables on the
> > > side.
> >
> > Yeah, that is a fair concern.  I'm not sure if I know of a good way to measure
> > it.  I took wide-int.ii and ran /usr/bin/time -v ./cc1plus; then it's roughly
> > Maximum resident set size (kbytes): 95020
> > vs.
> > Maximum resident set size (kbytes): 95272
> > which doesn't seem too bad but I don't know if it proves anything.
> >
> > If we went with the hash table, would it work like this?
> > 1) have a hash table mapping decls (key) to explicit-specifiers
> > 2) instead of setting DECL_EXPLICIT_SPEC put the parsed explicit-specifier
> >    into the table
> > 3) in tsubst_function_decl look if the fn decl is associated with any
> >    explicit-specifier, if it is, substitute it, and set DECL_NONCONVERTING_P
> >    accordingly.
> 
> Yes.  I think you want to use tree_cache_map so you don't have to
> worry about removing entries from the table if the decl is GC'd.

Done (along with the bit idea).

> > > > +/* Create a representation of the explicit-specifier with
> > > > +   constant-expression of EXPR.  COMPLAIN is as for tsubst.  */
> > > > +
> > > > +tree
> > > > +build_explicit_specifier (tree expr, tsubst_flags_t complain)
> > > > +{
> > > > +  if (processing_template_decl && value_dependent_expression_p (expr))
> > > > +    /* Wait for instantiation.  tsubst_function_decl will take care of it.  */
> > > > +    return expr;
> > > > +
> > > > +  expr = perform_implicit_conversion_flags (boolean_type_node, expr,
> > > > +                                           complain, LOOKUP_NORMAL);
> > > > +  expr = instantiate_non_dependent_expr (expr);
> > > > +  expr = cxx_constant_value (expr);
> > > > +  return expr;
> > > > +}
> > >
> > > Is there a reason not to use build_converted_constant_expr?
> >
> > build_converted_constant_expr doesn't allow narrowing conversions but we should
> > allow them in explicit-specifier which takes "contextually converted constant
> > expression of type bool", much like in
> >
> > constexpr int foo () { return 42; }
> > static_assert (foo());
> >
> > Right?
> 
> That's what the standard seems to require, but I think that's broken;
> I cc'd you on my email to the reflector.

Thanks.  Let's see how that pans; for now I've changed the patch to use
build_converted_constant_expr and tweaked the tests.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2018-10-03  Marek Polacek  <polacek@redhat.com>

	Implement P0892R2, explicit(bool).
	* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_explicit_bool.

	* call.c (add_template_candidate_real): Return if the declaration is
	explicit and we're only looking for non-converting constructor.
	* cp-tree.h (lang_decl_fn): Add has_dependent_explicit_spec_p bit.
	(DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P): New macro.
	(build_explicit_specifier, store_explicit_specifier): Declare.
	* decl.c (build_explicit_specifier): New function.
	* parser.c (cp_parser_decl_specifier_seq): Add explicit_specifier
	parameter.  Pass it down to cp_parser_function_specifier_opt.
	(cp_parser_function_specifier_opt): Add explicit_specifier parameter.
	<case RID_EXPLICIT>: Parse C++20 explicit(bool).
	(cp_parser_explicit_instantiation): Update call to
	cp_parser_function_specifier_opt.
	(cp_parser_member_declaration): Have cp_parser_decl_specifier_seq save
	the explicit-specifier.  Save it using store_explicit_specifier.
	(cp_parser_single_declaration): Likewise.
	* pt.c (store_explicit_specifier, lookup_explicit_specifier): New.
	(tsubst_function_decl): Handle explicit(dependent-expr).

	* g++.dg/cpp2a/explicit1.C: New test.
	* g++.dg/cpp2a/explicit10.C: New test.
	* g++.dg/cpp2a/explicit11.C: New test.
	* g++.dg/cpp2a/explicit2.C: New test.
	* g++.dg/cpp2a/explicit3.C: New test.
	* g++.dg/cpp2a/explicit4.C: New test.
	* g++.dg/cpp2a/explicit5.C: New test.
	* g++.dg/cpp2a/explicit6.C: New test.
	* g++.dg/cpp2a/explicit7.C: New test.
	* g++.dg/cpp2a/explicit8.C: New test.
	* g++.dg/cpp2a/explicit9.C: New test.

	* testsuite/20_util/any/cons/explicit.cc: Adjust dg-error.
	* testsuite/20_util/pair/cons/explicit_construct.cc: Likewise.
	* testsuite/20_util/tuple/cons/explicit_construct.cc: Likewise.

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

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

Marek
Jason Merrill Oct. 11, 2018, 3:35 p.m. UTC | #10
On Wed, Oct 3, 2018 at 7:11 PM Marek Polacek <polacek@redhat.com> wrote:
>
> On Wed, Oct 03, 2018 at 10:24:52AM -0400, Jason Merrill wrote:
> > On Tue, Oct 2, 2018 at 5:25 PM Marek Polacek <polacek@redhat.com> wrote:
> > >
> > > On Mon, Oct 01, 2018 at 07:47:10PM -0400, Jason Merrill wrote:
> > > > On Mon, Oct 1, 2018 at 6:41 PM Marek Polacek <polacek@redhat.com> wrote:
> > > > >
> > > > > This patch implements C++20 explicit(bool), as described in:
> > > > > <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0892r2.html>.
> > > > >
> > > > > I tried to follow the noexcept specifier implementation where I could, which
> > > > > made the non-template parts of this fairly easy.  To make explicit(expr) work
> > > > > with dependent expressions, I had to add DECL_EXPLICIT_SPEC to lang_decl_fn,
> > > > > which serves as a vessel to get the explicit-specifier to tsubst_function_decl
> > > > > where I substitute the dependent arguments.
> > > >
> > > > What's the impact of that on memory consumption?  I'm nervous about
> > > > adding another word to most functions when it's not useful to most of
> > > > them.  For several similar things we've been using hash tables on the
> > > > side.
> > >
> > > Yeah, that is a fair concern.  I'm not sure if I know of a good way to measure
> > > it.  I took wide-int.ii and ran /usr/bin/time -v ./cc1plus; then it's roughly
> > > Maximum resident set size (kbytes): 95020
> > > vs.
> > > Maximum resident set size (kbytes): 95272
> > > which doesn't seem too bad but I don't know if it proves anything.
> > >
> > > If we went with the hash table, would it work like this?
> > > 1) have a hash table mapping decls (key) to explicit-specifiers
> > > 2) instead of setting DECL_EXPLICIT_SPEC put the parsed explicit-specifier
> > >    into the table
> > > 3) in tsubst_function_decl look if the fn decl is associated with any
> > >    explicit-specifier, if it is, substitute it, and set DECL_NONCONVERTING_P
> > >    accordingly.
> >
> > Yes.  I think you want to use tree_cache_map so you don't have to
> > worry about removing entries from the table if the decl is GC'd.
>
> Done (along with the bit idea).

It occurs to me that it might be better to put all these sorts of
things in DECL_ATTRIBUTES instead, but that's definitely a question
for another day.

> +       /* [dcl.fct.spec]
> +          "the constant-expression, if supplied, shall be a contextually
> +          converted constant expression of type bool."  */
> +       expr = build_explicit_specifier (expr, tf_warning_or_error);
> +       /* We could evaluate it -- mark the decl as appropriate.  */
> +       if (expr == boolean_true_node)
> +         set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
> +       else if (explicit_specifier)
> +         /* The expression was value-dependent.  Remember it so that we can
> +            substitute it later.  */
> +         *explicit_specifier = expr;

What if expr == boolean_false_node?

> +      /* 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);
> +       }

What if spec is still dependent, e.g. after partial substitution of a
member template?

Jason
Marek Polacek Oct. 12, 2018, 12:25 a.m. UTC | #11
On Thu, Oct 11, 2018 at 11:35:23AM -0400, Jason Merrill wrote:
> > +       /* [dcl.fct.spec]
> > +          "the constant-expression, if supplied, shall be a contextually
> > +          converted constant expression of type bool."  */
> > +       expr = build_explicit_specifier (expr, tf_warning_or_error);
> > +       /* We could evaluate it -- mark the decl as appropriate.  */
> > +       if (expr == boolean_true_node)
> > +         set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
> > +       else if (explicit_specifier)
> > +         /* The expression was value-dependent.  Remember it so that we can
> > +            substitute it later.  */
> > +         *explicit_specifier = expr;
> 
> What if expr == boolean_false_node?

Then we proceed like no explicit was present and the decl isn't marked as
explicit/nonconverting.  Perhaps I could have made this clearer with

  else if (expr == boolean_true_node)
    /* Don't mark the decl as explicit.  */;

or somesuch.
 
> > +      /* 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);
> > +       }
> 
> What if spec is still dependent, e.g. after partial substitution of a
> member template?

Something like this?

template<typename> struct A {
  template<typename T, int N = 0>
  explicit(N) operator T();
};

void
bar ()
{
  A<int> a;
  int i = a;
}

This also seemed to work: if spec is still dependent, the decl isn't marked as
DECL_NONCONVERTING_P, and we'll try again after deduction (fn_type_unification
in add_template_candidate).

Marek
Jason Merrill Oct. 12, 2018, 6:26 a.m. UTC | #12
On Thu, Oct 11, 2018 at 8:25 PM Marek Polacek <polacek@redhat.com> wrote:
>
> On Thu, Oct 11, 2018 at 11:35:23AM -0400, Jason Merrill wrote:
> > > +       /* [dcl.fct.spec]
> > > +          "the constant-expression, if supplied, shall be a contextually
> > > +          converted constant expression of type bool."  */
> > > +       expr = build_explicit_specifier (expr, tf_warning_or_error);
> > > +       /* We could evaluate it -- mark the decl as appropriate.  */
> > > +       if (expr == boolean_true_node)
> > > +         set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
> > > +       else if (explicit_specifier)
> > > +         /* The expression was value-dependent.  Remember it so that we can
> > > +            substitute it later.  */
> > > +         *explicit_specifier = expr;
> >
> > What if expr == boolean_false_node?
>
> Then we proceed like no explicit was present and the decl isn't marked as
> explicit/nonconverting.  Perhaps I could have made this clearer with
>
>   else if (expr == boolean_true_node)
>     /* Don't mark the decl as explicit.  */;
>
> or somesuch.

Yes, and also so that we don't store the false as a "dependent" specifier.

> > > +      /* 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);
> > > +       }
> >
> > What if spec is still dependent, e.g. after partial substitution of a
> > member template?
>
> Something like this?
>
> template<typename> struct A {
>   template<typename T, int N = 0>
>   explicit(N) operator T();
> };
>
> void
> bar ()
> {
>   A<int> a;
>   int i = a;
> }
>
> This also seemed to work: if spec is still dependent, the decl isn't marked as
> DECL_NONCONVERTING_P, and we'll try again after deduction (fn_type_unification
> in add_template_candidate).

Does it also work if N is true?  What if the specifier depends on
template parameters from both the enclosing class and the member
template?

Jason
Marek Polacek Oct. 12, 2018, 4:32 p.m. UTC | #13
On Fri, Oct 12, 2018 at 02:26:45AM -0400, Jason Merrill wrote:
> On Thu, Oct 11, 2018 at 8:25 PM Marek Polacek <polacek@redhat.com> wrote:
> >
> > On Thu, Oct 11, 2018 at 11:35:23AM -0400, Jason Merrill wrote:
> > > > +       /* [dcl.fct.spec]
> > > > +          "the constant-expression, if supplied, shall be a contextually
> > > > +          converted constant expression of type bool."  */
> > > > +       expr = build_explicit_specifier (expr, tf_warning_or_error);
> > > > +       /* We could evaluate it -- mark the decl as appropriate.  */
> > > > +       if (expr == boolean_true_node)
> > > > +         set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
> > > > +       else if (explicit_specifier)
> > > > +         /* The expression was value-dependent.  Remember it so that we can
> > > > +            substitute it later.  */
> > > > +         *explicit_specifier = expr;
> > >
> > > What if expr == boolean_false_node?
> >
> > Then we proceed like no explicit was present and the decl isn't marked as
> > explicit/nonconverting.  Perhaps I could have made this clearer with
> >
> >   else if (expr == boolean_true_node)
> >     /* Don't mark the decl as explicit.  */;
> >
> > or somesuch.
> 
> Yes, and also so that we don't store the false as a "dependent" specifier.

Oh, absolutely.  Fixed.

> > > > +      /* 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);
> > > > +       }
> > >
> > > What if spec is still dependent, e.g. after partial substitution of a
> > > member template?
> >
> > Something like this?
> >
> > template<typename> struct A {
> >   template<typename T, int N = 0>
> >   explicit(N) operator T();
> > };
> >
> > void
> > bar ()
> > {
> >   A<int> a;
> >   int i = a;
> > }
> >
> > This also seemed to work: if spec is still dependent, the decl isn't marked as
> > DECL_NONCONVERTING_P, and we'll try again after deduction (fn_type_unification
> > in add_template_candidate).
> 
> Does it also work if N is true?  What if the specifier depends on
> template parameters from both the enclosing class and the member
> template?

All of that seems to work.  I've added explicit12.C and explicit13.C tests to
cover that.  Please check if that's what you had in mind.  Thanks,

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2018-10-12  Marek Polacek  <polacek@redhat.com>

	Implement P0892R2, explicit(bool).
	* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_explicit_bool.

	* call.c (add_template_candidate_real): Return if the declaration is
	explicit and we're only looking for non-converting constructor.
	* cp-tree.h (lang_decl_fn): Add has_dependent_explicit_spec_p bit.
	(DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P): New macro.
	(build_explicit_specifier, store_explicit_specifier): Declare.
	* decl.c (build_explicit_specifier): New function.
	* parser.c (cp_parser_decl_specifier_seq): Add explicit_specifier
	parameter.  Pass it down to cp_parser_function_specifier_opt.
	(cp_parser_function_specifier_opt): Add explicit_specifier parameter.
	<case RID_EXPLICIT>: Parse C++20 explicit(bool).
	(cp_parser_explicit_instantiation): Update call to
	cp_parser_function_specifier_opt.
	(cp_parser_member_declaration): Have cp_parser_decl_specifier_seq save
	the explicit-specifier.  Save it using store_explicit_specifier.
	(cp_parser_single_declaration): Likewise.
	* pt.c (store_explicit_specifier, lookup_explicit_specifier): New.
	(tsubst_function_decl): Handle explicit(dependent-expr).

	* g++.dg/cpp2a/explicit1.C: New test.
	* g++.dg/cpp2a/explicit10.C: New test.
	* g++.dg/cpp2a/explicit11.C: New test.
	* g++.dg/cpp2a/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 0baf26e4346..1ae2f0d68a2 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..cf2d0554cb7 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
@@ -6375,6 +6382,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 +6779,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..f7a2e2a6789 100644
--- gcc/gcc/cp/decl.c
+++ gcc/gcc/cp/decl.c
@@ -16560,4 +16560,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 76ff83616b0..29a63cd92f6 100644
--- gcc/gcc/cp/parser.c
+++ gcc/gcc/cp/parser.c
@@ -2140,11 +2140,11 @@ static void cp_parser_block_declaration
 static void cp_parser_simple_declaration
   (cp_parser *, bool, tree *);
 static void cp_parser_decl_specifier_seq
-  (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *);
+  (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *, tree * = NULL);
 static tree cp_parser_storage_class_specifier_opt
   (cp_parser *);
 static tree cp_parser_function_specifier_opt
-  (cp_parser *, cp_decl_specifier_seq *);
+  (cp_parser *, cp_decl_specifier_seq *, tree *);
 static tree cp_parser_type_specifier
   (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, bool,
    int *, bool *);
@@ -13513,13 +13513,15 @@ cp_parser_decomposition_declaration (cp_parser *parser,
      2: one of the decl-specifiers is an enum-specifier or a
 	class-specifier (i.e., a type definition)
 
-   */
+   EXPLICIT_SPECIFIER is used in case the explicit-specifier, if any, has
+   value-dependent expression.  */
 
 static void
 cp_parser_decl_specifier_seq (cp_parser* parser,
 			      cp_parser_flags flags,
 			      cp_decl_specifier_seq *decl_specs,
-			      int* declares_class_or_enum)
+			      int* declares_class_or_enum,
+			      tree* explicit_specifier)
 {
   bool constructor_possible_p = !parser->in_declarator_p;
   bool found_decl_spec = false;
@@ -13640,7 +13642,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_INLINE:
 	case RID_VIRTUAL:
 	case RID_EXPLICIT:
-	  cp_parser_function_specifier_opt (parser, decl_specs);
+	  cp_parser_function_specifier_opt (parser, decl_specs,
+					    explicit_specifier);
 	  break;
 
 	  /* decl-specifier:
@@ -13867,12 +13870,17 @@ cp_parser_storage_class_specifier_opt (cp_parser* parser)
      virtual
      explicit
 
+   C++2A Extension:
+     explicit(constant-expression)
+
    Returns an IDENTIFIER_NODE corresponding to the keyword used.
-   Updates DECL_SPECS, if it is non-NULL.  */
+   Updates DECL_SPECS, if it is non-NULL.  Updates EXPLICIT_SPECIFIER,
+   in case an explicit-specifier is found.  */
 
 static tree
 cp_parser_function_specifier_opt (cp_parser* parser,
-				  cp_decl_specifier_seq *decl_specs)
+				  cp_decl_specifier_seq *decl_specs,
+				  tree *explicit_specifier)
 {
   cp_token *token = cp_lexer_peek_token (parser->lexer);
   switch (token->keyword)
@@ -13893,8 +13901,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 (explicit_specifier)
+	  /* The expression was value-dependent.  Remember it so that we can
+	     substitute it later.  */
+	  *explicit_specifier = expr;
+	return id;
+      }
 
     default:
       return NULL_TREE;
@@ -16655,7 +16708,8 @@ cp_parser_explicit_instantiation (cp_parser* parser)
       if (!extension_specifier)
 	extension_specifier
 	  = cp_parser_function_specifier_opt (parser,
-					      /*decl_specs=*/NULL);
+					      /*decl_specs=*/NULL,
+					      /*explicit_specifier*/NULL);
     }
 
   /* Look for the `template' keyword.  */
@@ -23569,6 +23623,7 @@ cp_parser_member_declaration (cp_parser* parser)
   cp_token *initializer_token_start = NULL;
   int saved_pedantic;
   bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+  tree explicit_specifier = NULL_TREE;
 
   /* Check for the `__extension__' keyword.  */
   if (cp_parser_extension_opt (parser, &saved_pedantic))
@@ -23665,7 +23720,8 @@ cp_parser_member_declaration (cp_parser* parser)
   cp_parser_decl_specifier_seq (parser,
 				CP_PARSER_FLAGS_OPTIONAL,
 				&decl_specifiers,
-				&declares_class_or_enum);
+				&declares_class_or_enum,
+				&explicit_specifier);
   /* Check for an invalid type-name.  */
   if (!decl_specifiers.any_type_specifiers_p
       && cp_parser_parse_and_diagnose_invalid_type_name (parser))
@@ -24019,6 +24075,8 @@ cp_parser_member_declaration (cp_parser* parser)
 		  /* If the member was not a friend, declare it here.  */
 		  if (!friend_p)
 		    finish_member_declaration (decl);
+		  if (decl && explicit_specifier)
+		    store_explicit_specifier (decl, explicit_specifier);
 		  /* Peek at the next token.  */
 		  token = cp_lexer_peek_token (parser->lexer);
 		  /* If the next token is a semicolon, consume it.  */
@@ -24048,6 +24106,8 @@ cp_parser_member_declaration (cp_parser* parser)
 		  else
 		    decl = finish_fully_implicit_template (parser, decl);
 		}
+	      if (decl && explicit_specifier)
+		store_explicit_specifier (decl, explicit_specifier);
 	    }
 
 	  cp_finalize_omp_declare_simd (parser, decl);
@@ -27327,6 +27387,7 @@ cp_parser_single_declaration (cp_parser* parser,
   cp_decl_specifier_seq decl_specifiers;
   bool function_definition_p = false;
   cp_token *decl_spec_token_start;
+  tree explicit_specifier = NULL_TREE;
 
   /* This function is only used when processing a template
      declaration.  */
@@ -27342,7 +27403,8 @@ cp_parser_single_declaration (cp_parser* parser,
   cp_parser_decl_specifier_seq (parser,
 				CP_PARSER_FLAGS_OPTIONAL,
 				&decl_specifiers,
-				&declares_class_or_enum);
+				&declares_class_or_enum,
+				&explicit_specifier);
   if (friend_p)
     *friend_p = cp_parser_friend_p (&decl_specifiers);
 
@@ -27453,6 +27515,9 @@ cp_parser_single_declaration (cp_parser* parser,
 
     if (decl && VAR_P (decl))
       check_template_variable (decl);
+
+    if (decl && explicit_specifier)
+      store_explicit_specifier (decl, explicit_specifier);
     }
 
   /* Look for a trailing `;' after the declaration.  */
diff --git gcc/gcc/cp/pt.c gcc/gcc/cp/pt.c
index f290cb32fc2..c9a3bb0235d 100644
--- gcc/gcc/cp/pt.c
+++ gcc/gcc/cp/pt.c
@@ -12803,6 +12803,28 @@ tsubst_default_arguments (tree fn, tsubst_flags_t complain)
 						    complain);
 }
 
+/* Hash table mapping a FUNCTION_DECL to its dependent explicit-specifier.  */
+static GTY((cache)) tree_cache_map *explicit_specifier_map;
+
+/* Store a pair to EXPLICIT_SPECIFIER_MAP.  */
+
+void
+store_explicit_specifier (tree v, tree t)
+{
+  if (!explicit_specifier_map)
+    explicit_specifier_map = tree_cache_map::create_ggc (37);
+  DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (v) = true;
+  explicit_specifier_map->put (v, t);
+}
+
+/* Lookup an element in EXPLICIT_SPECIFIER_MAP.  */
+
+static tree
+lookup_explicit_specifier (tree v)
+{
+  return *explicit_specifier_map->get (v);
+}
+
 /* Subroutine of tsubst_decl for the case when T is a FUNCTION_DECL.  */
 
 static tree
@@ -12822,6 +12844,17 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
       if (!uses_template_parms (DECL_TI_ARGS (t)))
 	return t;
 
+      /* Handle explicit(dependent-expr).  */
+      if (DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (t))
+	{
+	  tree spec = lookup_explicit_specifier (t);
+	  spec = tsubst_copy_and_build (spec, args, complain, in_decl,
+					/*function_p=*/false,
+					/*i_c_e_p=*/true);
+	  spec = build_explicit_specifier (spec, complain);
+	  DECL_NONCONVERTING_P (t) = (spec == boolean_true_node);
+	}
+
       /* Calculate the most general template of which R is a
 	 specialization.  */
       gen_tmpl = most_general_template (DECL_TI_TEMPLATE (t));
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C
index e69de29bb2d..b39f90f3397 100644
--- gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C
+++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C
@@ -0,0 +1,63 @@
+// P0892R2
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+constexpr int fn0 () { return 0; }
+constexpr int fn1 () { return 1; }
+
+struct S {
+  explicit(true) S(int);
+  explicit(1 == 0) S(int, int);
+  explicit(fn0()) S(int, int, int);
+  explicit(fn1()) S(int, int, int, int);
+};
+
+struct X {
+  static const bool value = true;
+  static constexpr bool foo () { return 1; }
+};
+
+struct T {
+  explicit(true ? 1 : throw 1) T(int);
+  explicit(true || true ? 1 : throw 1) T(int, int);
+  explicit(X::value) T(int, int, int);
+  explicit(X::foo ()) T(int, int, int, int);
+};
+
+struct W {
+  constexpr operator bool() { return true; };
+};
+
+struct W2 {
+  constexpr operator bool() { return false; };
+};
+
+struct U {
+  explicit(W()) U(int);
+  explicit(W2()) U(int, int);
+};
+
+int
+main ()
+{
+  S s1 = { 1 }; // { dg-error "converting" }
+  S s1x{ 1 };
+  S s2 = { 2, 3 };
+  S s3 = { 4, 5, 6 };
+  S s4 = { 7, 8, 9, 10 }; // { dg-error "converting" }
+  S s4x{ 7, 8, 9, 10 };
+
+  T t1 = { 1 }; // { dg-error "converting" }
+  T t2 = { 1, 2 }; // { dg-error "converting" }
+  T t3 = { 1, 2, 3 }; // { dg-error "converting" }
+  T t4 = { 1, 2, 3, 4 }; // { dg-error "converting" }
+  T t5{ 1 };
+  T t6{ 1, 2 };
+  T t7{ 1, 2, 3 };
+  T t8{ 1, 2, 3, 4 };
+
+  U u1 = { 1 }; // { dg-error "converting" }
+  U u2{ 1 };
+  U u3 = { 1, 2 };
+  U u4 { 1, 2 };
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
index e69de29bb2d..c8701551c9a 100644
--- gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
+++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
@@ -0,0 +1,32 @@
+// P0892R2
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+#include <type_traits>
+
+class A {};
+class B : public A {};
+class C {};
+class D { public: operator C() { return c; }  C c; };
+
+template <typename T1, typename T2>
+struct S {
+  explicit(!std::is_convertible_v<T1, T2>)
+  S(T1, T2) { }
+};
+
+void
+foo ()
+{
+  A a;
+  B b;
+  C c;
+  D d;
+
+  S<int, int> s{ 1, 2 };
+  S<int, int> s2 = { 1, 2 };
+  S<B*, A*> s3 = { &b, &a };
+  S<A*, B*> s4 = { &a, &b }; // { dg-error "converting" }
+  S<B*, C*> s5 = { &b, &c }; // { dg-error "converting" }
+  S<D, C> s6 = { d, c };
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
index e69de29bb2d..ad1bed5b3f0 100644
--- gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
+++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
@@ -0,0 +1,29 @@
+// P0892R2
+// { dg-do compile }
+// { dg-options "-std=c++2a -pedantic" }
+
+template<typename T>
+struct A {
+  explicit A(const T&, ...) noexcept;
+  A(T&&, ...);
+};
+
+int i;
+A a1 = { i, i }; // { dg-error "deduction|cannot" }
+A a2{ i, i };
+A a3{ 0, i };
+A a4 = { 0, i };
+
+template<typename T> A(const T&, const T&) -> A<T&>;
+template<typename T> explicit A(T&&, T&&) -> A<T>;
+
+A a5 = { 0, 1 }; // { dg-error "deduction|ambiguous" }
+A a6{ 0, 1 };
+
+template<typename T>
+struct B {
+  template<typename U> using TA = T;
+  template<typename U> B(U, TA<U>);
+};
+
+B b{(int *)0, (char *)0};
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/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>{});
Marek Polacek Oct. 19, 2018, 5:28 p.m. UTC | #14
Ping.

On Fri, Oct 12, 2018 at 12:32:43PM -0400, Marek Polacek wrote:
> On Fri, Oct 12, 2018 at 02:26:45AM -0400, Jason Merrill wrote:
> > On Thu, Oct 11, 2018 at 8:25 PM Marek Polacek <polacek@redhat.com> wrote:
> > >
> > > On Thu, Oct 11, 2018 at 11:35:23AM -0400, Jason Merrill wrote:
> > > > > +       /* [dcl.fct.spec]
> > > > > +          "the constant-expression, if supplied, shall be a contextually
> > > > > +          converted constant expression of type bool."  */
> > > > > +       expr = build_explicit_specifier (expr, tf_warning_or_error);
> > > > > +       /* We could evaluate it -- mark the decl as appropriate.  */
> > > > > +       if (expr == boolean_true_node)
> > > > > +         set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
> > > > > +       else if (explicit_specifier)
> > > > > +         /* The expression was value-dependent.  Remember it so that we can
> > > > > +            substitute it later.  */
> > > > > +         *explicit_specifier = expr;
> > > >
> > > > What if expr == boolean_false_node?
> > >
> > > Then we proceed like no explicit was present and the decl isn't marked as
> > > explicit/nonconverting.  Perhaps I could have made this clearer with
> > >
> > >   else if (expr == boolean_true_node)
> > >     /* Don't mark the decl as explicit.  */;
> > >
> > > or somesuch.
> > 
> > Yes, and also so that we don't store the false as a "dependent" specifier.
> 
> Oh, absolutely.  Fixed.
> 
> > > > > +      /* 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);
> > > > > +       }
> > > >
> > > > What if spec is still dependent, e.g. after partial substitution of a
> > > > member template?
> > >
> > > Something like this?
> > >
> > > template<typename> struct A {
> > >   template<typename T, int N = 0>
> > >   explicit(N) operator T();
> > > };
> > >
> > > void
> > > bar ()
> > > {
> > >   A<int> a;
> > >   int i = a;
> > > }
> > >
> > > This also seemed to work: if spec is still dependent, the decl isn't marked as
> > > DECL_NONCONVERTING_P, and we'll try again after deduction (fn_type_unification
> > > in add_template_candidate).
> > 
> > Does it also work if N is true?  What if the specifier depends on
> > template parameters from both the enclosing class and the member
> > template?
> 
> All of that seems to work.  I've added explicit12.C and explicit13.C tests to
> cover that.  Please check if that's what you had in mind.  Thanks,
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2018-10-12  Marek Polacek  <polacek@redhat.com>
> 
> 	Implement P0892R2, explicit(bool).
> 	* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_explicit_bool.
> 
> 	* call.c (add_template_candidate_real): Return if the declaration is
> 	explicit and we're only looking for non-converting constructor.
> 	* cp-tree.h (lang_decl_fn): Add has_dependent_explicit_spec_p bit.
> 	(DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P): New macro.
> 	(build_explicit_specifier, store_explicit_specifier): Declare.
> 	* decl.c (build_explicit_specifier): New function.
> 	* parser.c (cp_parser_decl_specifier_seq): Add explicit_specifier
> 	parameter.  Pass it down to cp_parser_function_specifier_opt.
> 	(cp_parser_function_specifier_opt): Add explicit_specifier parameter.
> 	<case RID_EXPLICIT>: Parse C++20 explicit(bool).
> 	(cp_parser_explicit_instantiation): Update call to
> 	cp_parser_function_specifier_opt.
> 	(cp_parser_member_declaration): Have cp_parser_decl_specifier_seq save
> 	the explicit-specifier.  Save it using store_explicit_specifier.
> 	(cp_parser_single_declaration): Likewise.
> 	* pt.c (store_explicit_specifier, lookup_explicit_specifier): New.
> 	(tsubst_function_decl): Handle explicit(dependent-expr).
> 
> 	* g++.dg/cpp2a/explicit1.C: New test.
> 	* g++.dg/cpp2a/explicit10.C: New test.
> 	* g++.dg/cpp2a/explicit11.C: New test.
> 	* g++.dg/cpp2a/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 0baf26e4346..1ae2f0d68a2 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..cf2d0554cb7 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
> @@ -6375,6 +6382,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 +6779,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..f7a2e2a6789 100644
> --- gcc/gcc/cp/decl.c
> +++ gcc/gcc/cp/decl.c
> @@ -16560,4 +16560,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 76ff83616b0..29a63cd92f6 100644
> --- gcc/gcc/cp/parser.c
> +++ gcc/gcc/cp/parser.c
> @@ -2140,11 +2140,11 @@ static void cp_parser_block_declaration
>  static void cp_parser_simple_declaration
>    (cp_parser *, bool, tree *);
>  static void cp_parser_decl_specifier_seq
> -  (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *);
> +  (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *, tree * = NULL);
>  static tree cp_parser_storage_class_specifier_opt
>    (cp_parser *);
>  static tree cp_parser_function_specifier_opt
> -  (cp_parser *, cp_decl_specifier_seq *);
> +  (cp_parser *, cp_decl_specifier_seq *, tree *);
>  static tree cp_parser_type_specifier
>    (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, bool,
>     int *, bool *);
> @@ -13513,13 +13513,15 @@ cp_parser_decomposition_declaration (cp_parser *parser,
>       2: one of the decl-specifiers is an enum-specifier or a
>  	class-specifier (i.e., a type definition)
>  
> -   */
> +   EXPLICIT_SPECIFIER is used in case the explicit-specifier, if any, has
> +   value-dependent expression.  */
>  
>  static void
>  cp_parser_decl_specifier_seq (cp_parser* parser,
>  			      cp_parser_flags flags,
>  			      cp_decl_specifier_seq *decl_specs,
> -			      int* declares_class_or_enum)
> +			      int* declares_class_or_enum,
> +			      tree* explicit_specifier)
>  {
>    bool constructor_possible_p = !parser->in_declarator_p;
>    bool found_decl_spec = false;
> @@ -13640,7 +13642,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
>  	case RID_INLINE:
>  	case RID_VIRTUAL:
>  	case RID_EXPLICIT:
> -	  cp_parser_function_specifier_opt (parser, decl_specs);
> +	  cp_parser_function_specifier_opt (parser, decl_specs,
> +					    explicit_specifier);
>  	  break;
>  
>  	  /* decl-specifier:
> @@ -13867,12 +13870,17 @@ cp_parser_storage_class_specifier_opt (cp_parser* parser)
>       virtual
>       explicit
>  
> +   C++2A Extension:
> +     explicit(constant-expression)
> +
>     Returns an IDENTIFIER_NODE corresponding to the keyword used.
> -   Updates DECL_SPECS, if it is non-NULL.  */
> +   Updates DECL_SPECS, if it is non-NULL.  Updates EXPLICIT_SPECIFIER,
> +   in case an explicit-specifier is found.  */
>  
>  static tree
>  cp_parser_function_specifier_opt (cp_parser* parser,
> -				  cp_decl_specifier_seq *decl_specs)
> +				  cp_decl_specifier_seq *decl_specs,
> +				  tree *explicit_specifier)
>  {
>    cp_token *token = cp_lexer_peek_token (parser->lexer);
>    switch (token->keyword)
> @@ -13893,8 +13901,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 (explicit_specifier)
> +	  /* The expression was value-dependent.  Remember it so that we can
> +	     substitute it later.  */
> +	  *explicit_specifier = expr;
> +	return id;
> +      }
>  
>      default:
>        return NULL_TREE;
> @@ -16655,7 +16708,8 @@ cp_parser_explicit_instantiation (cp_parser* parser)
>        if (!extension_specifier)
>  	extension_specifier
>  	  = cp_parser_function_specifier_opt (parser,
> -					      /*decl_specs=*/NULL);
> +					      /*decl_specs=*/NULL,
> +					      /*explicit_specifier*/NULL);
>      }
>  
>    /* Look for the `template' keyword.  */
> @@ -23569,6 +23623,7 @@ cp_parser_member_declaration (cp_parser* parser)
>    cp_token *initializer_token_start = NULL;
>    int saved_pedantic;
>    bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
> +  tree explicit_specifier = NULL_TREE;
>  
>    /* Check for the `__extension__' keyword.  */
>    if (cp_parser_extension_opt (parser, &saved_pedantic))
> @@ -23665,7 +23720,8 @@ cp_parser_member_declaration (cp_parser* parser)
>    cp_parser_decl_specifier_seq (parser,
>  				CP_PARSER_FLAGS_OPTIONAL,
>  				&decl_specifiers,
> -				&declares_class_or_enum);
> +				&declares_class_or_enum,
> +				&explicit_specifier);
>    /* Check for an invalid type-name.  */
>    if (!decl_specifiers.any_type_specifiers_p
>        && cp_parser_parse_and_diagnose_invalid_type_name (parser))
> @@ -24019,6 +24075,8 @@ cp_parser_member_declaration (cp_parser* parser)
>  		  /* If the member was not a friend, declare it here.  */
>  		  if (!friend_p)
>  		    finish_member_declaration (decl);
> +		  if (decl && explicit_specifier)
> +		    store_explicit_specifier (decl, explicit_specifier);
>  		  /* Peek at the next token.  */
>  		  token = cp_lexer_peek_token (parser->lexer);
>  		  /* If the next token is a semicolon, consume it.  */
> @@ -24048,6 +24106,8 @@ cp_parser_member_declaration (cp_parser* parser)
>  		  else
>  		    decl = finish_fully_implicit_template (parser, decl);
>  		}
> +	      if (decl && explicit_specifier)
> +		store_explicit_specifier (decl, explicit_specifier);
>  	    }
>  
>  	  cp_finalize_omp_declare_simd (parser, decl);
> @@ -27327,6 +27387,7 @@ cp_parser_single_declaration (cp_parser* parser,
>    cp_decl_specifier_seq decl_specifiers;
>    bool function_definition_p = false;
>    cp_token *decl_spec_token_start;
> +  tree explicit_specifier = NULL_TREE;
>  
>    /* This function is only used when processing a template
>       declaration.  */
> @@ -27342,7 +27403,8 @@ cp_parser_single_declaration (cp_parser* parser,
>    cp_parser_decl_specifier_seq (parser,
>  				CP_PARSER_FLAGS_OPTIONAL,
>  				&decl_specifiers,
> -				&declares_class_or_enum);
> +				&declares_class_or_enum,
> +				&explicit_specifier);
>    if (friend_p)
>      *friend_p = cp_parser_friend_p (&decl_specifiers);
>  
> @@ -27453,6 +27515,9 @@ cp_parser_single_declaration (cp_parser* parser,
>  
>      if (decl && VAR_P (decl))
>        check_template_variable (decl);
> +
> +    if (decl && explicit_specifier)
> +      store_explicit_specifier (decl, explicit_specifier);
>      }
>  
>    /* Look for a trailing `;' after the declaration.  */
> diff --git gcc/gcc/cp/pt.c gcc/gcc/cp/pt.c
> index f290cb32fc2..c9a3bb0235d 100644
> --- gcc/gcc/cp/pt.c
> +++ gcc/gcc/cp/pt.c
> @@ -12803,6 +12803,28 @@ tsubst_default_arguments (tree fn, tsubst_flags_t complain)
>  						    complain);
>  }
>  
> +/* Hash table mapping a FUNCTION_DECL to its dependent explicit-specifier.  */
> +static GTY((cache)) tree_cache_map *explicit_specifier_map;
> +
> +/* Store a pair to EXPLICIT_SPECIFIER_MAP.  */
> +
> +void
> +store_explicit_specifier (tree v, tree t)
> +{
> +  if (!explicit_specifier_map)
> +    explicit_specifier_map = tree_cache_map::create_ggc (37);
> +  DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (v) = true;
> +  explicit_specifier_map->put (v, t);
> +}
> +
> +/* Lookup an element in EXPLICIT_SPECIFIER_MAP.  */
> +
> +static tree
> +lookup_explicit_specifier (tree v)
> +{
> +  return *explicit_specifier_map->get (v);
> +}
> +
>  /* Subroutine of tsubst_decl for the case when T is a FUNCTION_DECL.  */
>  
>  static tree
> @@ -12822,6 +12844,17 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
>        if (!uses_template_parms (DECL_TI_ARGS (t)))
>  	return t;
>  
> +      /* Handle explicit(dependent-expr).  */
> +      if (DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P (t))
> +	{
> +	  tree spec = lookup_explicit_specifier (t);
> +	  spec = tsubst_copy_and_build (spec, args, complain, in_decl,
> +					/*function_p=*/false,
> +					/*i_c_e_p=*/true);
> +	  spec = build_explicit_specifier (spec, complain);
> +	  DECL_NONCONVERTING_P (t) = (spec == boolean_true_node);
> +	}
> +
>        /* Calculate the most general template of which R is a
>  	 specialization.  */
>        gen_tmpl = most_general_template (DECL_TI_TEMPLATE (t));
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C
> index e69de29bb2d..b39f90f3397 100644
> --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C
> @@ -0,0 +1,63 @@
> +// P0892R2
> +// { dg-do compile }
> +// { dg-options "-std=c++2a" }
> +
> +constexpr int fn0 () { return 0; }
> +constexpr int fn1 () { return 1; }
> +
> +struct S {
> +  explicit(true) S(int);
> +  explicit(1 == 0) S(int, int);
> +  explicit(fn0()) S(int, int, int);
> +  explicit(fn1()) S(int, int, int, int);
> +};
> +
> +struct X {
> +  static const bool value = true;
> +  static constexpr bool foo () { return 1; }
> +};
> +
> +struct T {
> +  explicit(true ? 1 : throw 1) T(int);
> +  explicit(true || true ? 1 : throw 1) T(int, int);
> +  explicit(X::value) T(int, int, int);
> +  explicit(X::foo ()) T(int, int, int, int);
> +};
> +
> +struct W {
> +  constexpr operator bool() { return true; };
> +};
> +
> +struct W2 {
> +  constexpr operator bool() { return false; };
> +};
> +
> +struct U {
> +  explicit(W()) U(int);
> +  explicit(W2()) U(int, int);
> +};
> +
> +int
> +main ()
> +{
> +  S s1 = { 1 }; // { dg-error "converting" }
> +  S s1x{ 1 };
> +  S s2 = { 2, 3 };
> +  S s3 = { 4, 5, 6 };
> +  S s4 = { 7, 8, 9, 10 }; // { dg-error "converting" }
> +  S s4x{ 7, 8, 9, 10 };
> +
> +  T t1 = { 1 }; // { dg-error "converting" }
> +  T t2 = { 1, 2 }; // { dg-error "converting" }
> +  T t3 = { 1, 2, 3 }; // { dg-error "converting" }
> +  T t4 = { 1, 2, 3, 4 }; // { dg-error "converting" }
> +  T t5{ 1 };
> +  T t6{ 1, 2 };
> +  T t7{ 1, 2, 3 };
> +  T t8{ 1, 2, 3, 4 };
> +
> +  U u1 = { 1 }; // { dg-error "converting" }
> +  U u2{ 1 };
> +  U u3 = { 1, 2 };
> +  U u4 { 1, 2 };
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
> index e69de29bb2d..c8701551c9a 100644
> --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
> @@ -0,0 +1,32 @@
> +// P0892R2
> +// { dg-do compile }
> +// { dg-options "-std=c++2a" }
> +
> +#include <type_traits>
> +
> +class A {};
> +class B : public A {};
> +class C {};
> +class D { public: operator C() { return c; }  C c; };
> +
> +template <typename T1, typename T2>
> +struct S {
> +  explicit(!std::is_convertible_v<T1, T2>)
> +  S(T1, T2) { }
> +};
> +
> +void
> +foo ()
> +{
> +  A a;
> +  B b;
> +  C c;
> +  D d;
> +
> +  S<int, int> s{ 1, 2 };
> +  S<int, int> s2 = { 1, 2 };
> +  S<B*, A*> s3 = { &b, &a };
> +  S<A*, B*> s4 = { &a, &b }; // { dg-error "converting" }
> +  S<B*, C*> s5 = { &b, &c }; // { dg-error "converting" }
> +  S<D, C> s6 = { d, c };
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
> index e69de29bb2d..ad1bed5b3f0 100644
> --- gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
> @@ -0,0 +1,29 @@
> +// P0892R2
> +// { dg-do compile }
> +// { dg-options "-std=c++2a -pedantic" }
> +
> +template<typename T>
> +struct A {
> +  explicit A(const T&, ...) noexcept;
> +  A(T&&, ...);
> +};
> +
> +int i;
> +A a1 = { i, i }; // { dg-error "deduction|cannot" }
> +A a2{ i, i };
> +A a3{ 0, i };
> +A a4 = { 0, i };
> +
> +template<typename T> A(const T&, const T&) -> A<T&>;
> +template<typename T> explicit A(T&&, T&&) -> A<T>;
> +
> +A a5 = { 0, 1 }; // { dg-error "deduction|ambiguous" }
> +A a6{ 0, 1 };
> +
> +template<typename T>
> +struct B {
> +  template<typename U> using TA = T;
> +  template<typename U> B(U, TA<U>);
> +};
> +
> +B b{(int *)0, (char *)0};
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/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>{});

Marek
Jason Merrill Oct. 24, 2018, 6:55 p.m. UTC | #15
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.

> @@ -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.

Jason
Marek Polacek Oct. 29, 2018, 10:15 p.m. UTC | #16
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  <polacek@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>{});
Jason Merrill Oct. 30, 2018, 7:44 p.m. UTC | #17
On 10/29/18 6:15 PM, Marek Polacek wrote:
> 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!

It worked for the testcase because that bit was then copied to the 
instantiation.  I'm surprised that converting b0 in explicit13.C worked, 
though.

> +  /* 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);
> +    }

It still surprises me that you don't need to store the partially 
instantiated explicit-specifier, but explicit13.C does seem to cover the 
cases I would expect to break.

> Bootstrapped/regtested on x86_64-linux, ok for trunk?

OK.

Jason
diff mbox series

Patch

diff --git gcc/gcc/c-family/c-cppbuiltin.c gcc/gcc/c-family/c-cppbuiltin.c
index 96a6b4dfd2b..b085cf9201f 100644
--- gcc/gcc/c-family/c-cppbuiltin.c
+++ gcc/gcc/c-family/c-cppbuiltin.c
@@ -955,7 +955,7 @@  c_cpp_builtins (cpp_reader *pfile)
 	}
       if (cxx_dialect > cxx14)
 	{
-	  /* Set feature test macros for C++1z.  */
+	  /* Set feature test macros for C++17.  */
 	  cpp_define (pfile, "__cpp_unicode_characters=201411");
 	  cpp_define (pfile, "__cpp_static_assert=201411");
 	  cpp_define (pfile, "__cpp_namespace_attributes=201411");
@@ -975,6 +975,11 @@  c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_structured_bindings=201606");
 	  cpp_define (pfile, "__cpp_variadic_using=201611");
 	}
+      if (cxx_dialect > cxx17)
+	{
+	  /* Set feature test macros for C++2a.  */
+	  cpp_define (pfile, "__cpp_explicit_bool=201806");
+	}
       if (flag_concepts)
 	cpp_define (pfile, "__cpp_concepts=201507");
       if (flag_tm)
diff --git gcc/gcc/cp/call.c gcc/gcc/cp/call.c
index b2ca667c8b4..7003a4a2f50 100644
--- gcc/gcc/cp/call.c
+++ gcc/gcc/cp/call.c
@@ -3251,6 +3251,12 @@  add_template_candidate_real (struct z_candidate **candidates, tree tmpl,
       goto fail;
     }
 
+  /* Now the explicit specifier might have been deduced; check if this
+     declaration is explicit.  If it is and we're ignoring non-converting
+     constructors, don't add this function to the set of candidates.  */
+  if ((flags & LOOKUP_ONLYCONVERTING) && DECL_NONCONVERTING_P (fn))
+    return NULL;
+
   if (DECL_CONSTRUCTOR_P (fn) && nargs == 2)
     {
       tree arg_types = FUNCTION_FIRST_USER_PARMTYPE (fn);
diff --git gcc/gcc/cp/cp-tree.h gcc/gcc/cp/cp-tree.h
index efbdad83966..6cee257b8a7 100644
--- gcc/gcc/cp/cp-tree.h
+++ gcc/gcc/cp/cp-tree.h
@@ -2604,6 +2604,10 @@  struct GTY(()) lang_decl_fn {
      will be chained on the return pointer thunk.  */
   tree context;
 
+  /* Explicit-specifier, if any.  Only constructors or conversion
+     functions can have it.  */
+  tree explicit_specifier;
+
   union lang_decl_u5
   {
     /* In a non-thunk FUNCTION_DECL or TEMPLATE_DECL, this is
@@ -4501,6 +4505,10 @@  more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_GLOBAL_DTOR_P(NODE) \
   (LANG_DECL_FN_CHECK (NODE)->global_dtor_p)
 
+/* Explicit-specifier for this FUNCTION_DECL.  */
+#define DECL_EXPLICIT_SPEC(NODE) \
+  (LANG_DECL_FN_CHECK (NODE)->explicit_specifier)
+
 /* Accessor macros for C++ template decl nodes.  */
 
 /* The DECL_TEMPLATE_PARMS are a list.  The TREE_PURPOSE of each node
@@ -6366,6 +6374,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);
diff --git gcc/gcc/cp/decl.c gcc/gcc/cp/decl.c
index 2d9d56ef6e1..1a783d556de 100644
--- gcc/gcc/cp/decl.c
+++ gcc/gcc/cp/decl.c
@@ -16554,4 +16554,21 @@  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 take care of it.  */
+    return expr;
+
+  expr = perform_implicit_conversion_flags (boolean_type_node, expr,
+					    complain, LOOKUP_NORMAL);
+  expr = instantiate_non_dependent_expr (expr);
+  expr = cxx_constant_value (expr);
+  return expr;
+}
+
 #include "gt-cp-decl.h"
diff --git gcc/gcc/cp/parser.c gcc/gcc/cp/parser.c
index 6696f174d75..b248a51e80f 100644
--- gcc/gcc/cp/parser.c
+++ gcc/gcc/cp/parser.c
@@ -2141,11 +2141,11 @@  static void cp_parser_block_declaration
 static void cp_parser_simple_declaration
   (cp_parser *, bool, tree *);
 static void cp_parser_decl_specifier_seq
-  (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *);
+  (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *, tree * = NULL);
 static tree cp_parser_storage_class_specifier_opt
   (cp_parser *);
 static tree cp_parser_function_specifier_opt
-  (cp_parser *, cp_decl_specifier_seq *);
+  (cp_parser *, cp_decl_specifier_seq *, tree *);
 static tree cp_parser_type_specifier
   (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, bool,
    int *, bool *);
@@ -13515,13 +13515,15 @@  cp_parser_decomposition_declaration (cp_parser *parser,
      2: one of the decl-specifiers is an enum-specifier or a
 	class-specifier (i.e., a type definition)
 
-   */
+   EXPLICIT_SPECIFIER is used in case the explicit-specifier, if any, has
+   value-dependent expression.  */
 
 static void
 cp_parser_decl_specifier_seq (cp_parser* parser,
 			      cp_parser_flags flags,
 			      cp_decl_specifier_seq *decl_specs,
-			      int* declares_class_or_enum)
+			      int* declares_class_or_enum,
+			      tree* explicit_specifier)
 {
   bool constructor_possible_p = !parser->in_declarator_p;
   bool found_decl_spec = false;
@@ -13642,7 +13644,8 @@  cp_parser_decl_specifier_seq (cp_parser* parser,
 	case RID_INLINE:
 	case RID_VIRTUAL:
 	case RID_EXPLICIT:
-	  cp_parser_function_specifier_opt (parser, decl_specs);
+	  cp_parser_function_specifier_opt (parser, decl_specs,
+					    explicit_specifier);
 	  break;
 
 	  /* decl-specifier:
@@ -13869,12 +13872,17 @@  cp_parser_storage_class_specifier_opt (cp_parser* parser)
      virtual
      explicit
 
+   C++2A Extension:
+     explicit(constant-expression)
+
    Returns an IDENTIFIER_NODE corresponding to the keyword used.
-   Updates DECL_SPECS, if it is non-NULL.  */
+   Updates DECL_SPECS, if it is non-NULL.  Updates EXPLICIT_SPECIFIER,
+   in case an explicit-specifier is found.  */
 
 static tree
 cp_parser_function_specifier_opt (cp_parser* parser,
-				  cp_decl_specifier_seq *decl_specs)
+				  cp_decl_specifier_seq *decl_specs,
+				  tree *explicit_specifier)
 {
   cp_token *token = cp_lexer_peek_token (parser->lexer);
   switch (token->keyword)
@@ -13895,8 +13903,51 @@  cp_parser_function_specifier_opt (cp_parser* parser,
       break;
 
     case RID_EXPLICIT:
-      set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
-      break;
+      {
+	tree id = cp_lexer_consume_token (parser->lexer)->u.value;
+	/* If we see '(', it's C++20 explicit(bool).  */
+	tree expr;
+	if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+	  {
+	    matching_parens parens;
+	    parens.consume_open (parser);
+
+	    /* New types are not allowed in an explicit-specifier.  */
+	    const char *saved_message
+	      = parser->type_definition_forbidden_message;
+	    parser->type_definition_forbidden_message
+	      = G_("types may not be defined in explicit-specifier");
+
+	    if (cxx_dialect < cxx2a)
+	      pedwarn (token->location, 0,
+		       "%<explicit(bool)%> only available with -std=c++2a "
+		       "or -std=gnu++2a");
+
+	    /* Parse the constant-expression.  */
+	    expr = cp_parser_constant_expression (parser);
+
+	    /* Restore the saved message.  */
+	    parser->type_definition_forbidden_message = saved_message;
+	    parens.require_close (parser);
+	  }
+	else
+	  /* The explicit-specifier explicit without a constant-expression is
+	     equivalent to the explicit-specifier explicit(true).  */
+	  expr = boolean_true_node;
+
+	/* [dcl.fct.spec]
+	   "the constant-expression, if supplied, shall be a contextually
+	   converted constant expression of type bool."  */
+	expr = build_explicit_specifier (expr, tf_warning_or_error);
+	/* We could evaluate it -- mark the decl as appropriate.  */
+	if (expr == boolean_true_node)
+	  set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
+	else if (explicit_specifier)
+	  /* The expression was value-dependent.  Remember it so that we can
+	     substitute it later.  */
+	  *explicit_specifier = expr;
+	return id;
+      }
 
     default:
       return NULL_TREE;
@@ -16657,7 +16708,8 @@  cp_parser_explicit_instantiation (cp_parser* parser)
       if (!extension_specifier)
 	extension_specifier
 	  = cp_parser_function_specifier_opt (parser,
-					      /*decl_specs=*/NULL);
+					      /*decl_specs=*/NULL,
+					      /*explicit_specifier*/NULL);
     }
 
   /* Look for the `template' keyword.  */
@@ -23567,6 +23619,7 @@  cp_parser_member_declaration (cp_parser* parser)
   cp_token *initializer_token_start = NULL;
   int saved_pedantic;
   bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
+  tree explicit_specifier = NULL_TREE;
 
   /* Check for the `__extension__' keyword.  */
   if (cp_parser_extension_opt (parser, &saved_pedantic))
@@ -23663,7 +23716,8 @@  cp_parser_member_declaration (cp_parser* parser)
   cp_parser_decl_specifier_seq (parser,
 				CP_PARSER_FLAGS_OPTIONAL,
 				&decl_specifiers,
-				&declares_class_or_enum);
+				&declares_class_or_enum,
+				&explicit_specifier);
   /* Check for an invalid type-name.  */
   if (!decl_specifiers.any_type_specifiers_p
       && cp_parser_parse_and_diagnose_invalid_type_name (parser))
@@ -24017,6 +24071,8 @@  cp_parser_member_declaration (cp_parser* parser)
 		  /* If the member was not a friend, declare it here.  */
 		  if (!friend_p)
 		    finish_member_declaration (decl);
+		  if (decl && explicit_specifier)
+		    DECL_EXPLICIT_SPEC (decl) = explicit_specifier;
 		  /* Peek at the next token.  */
 		  token = cp_lexer_peek_token (parser->lexer);
 		  /* If the next token is a semicolon, consume it.  */
@@ -24046,6 +24102,8 @@  cp_parser_member_declaration (cp_parser* parser)
 		  else
 		    decl = finish_fully_implicit_template (parser, decl);
 		}
+	      if (decl && explicit_specifier)
+		DECL_EXPLICIT_SPEC (decl) = explicit_specifier;
 	    }
 
 	  cp_finalize_omp_declare_simd (parser, decl);
@@ -27325,6 +27383,7 @@  cp_parser_single_declaration (cp_parser* parser,
   cp_decl_specifier_seq decl_specifiers;
   bool function_definition_p = false;
   cp_token *decl_spec_token_start;
+  tree explicit_specifier = NULL_TREE;
 
   /* This function is only used when processing a template
      declaration.  */
@@ -27340,7 +27399,8 @@  cp_parser_single_declaration (cp_parser* parser,
   cp_parser_decl_specifier_seq (parser,
 				CP_PARSER_FLAGS_OPTIONAL,
 				&decl_specifiers,
-				&declares_class_or_enum);
+				&declares_class_or_enum,
+				&explicit_specifier);
   if (friend_p)
     *friend_p = cp_parser_friend_p (&decl_specifiers);
 
@@ -27451,6 +27511,9 @@  cp_parser_single_declaration (cp_parser* parser,
 
     if (decl && VAR_P (decl))
       check_template_variable (decl);
+
+    if (decl && explicit_specifier)
+      DECL_EXPLICIT_SPEC (decl) = explicit_specifier;
     }
 
   /* Look for a trailing `;' after the declaration.  */
diff --git gcc/gcc/cp/pt.c gcc/gcc/cp/pt.c
index b8b6545434b..3a7424ffb0f 100644
--- gcc/gcc/cp/pt.c
+++ gcc/gcc/cp/pt.c
@@ -12822,6 +12822,18 @@  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).  */
+      tree explicit_spec = DECL_EXPLICIT_SPEC (t);
+      if (explicit_spec)
+	{
+	  explicit_spec = tsubst_copy_and_build (explicit_spec, args, complain,
+						 in_decl,
+						 /*function_p=*/false,
+						 /*i_c_e_p=*/true);
+	  explicit_spec = build_explicit_specifier (explicit_spec, complain);
+	  DECL_NONCONVERTING_P (t) = (explicit_spec == boolean_true_node);
+	}
+
       /* Calculate the most general template of which R is a
 	 specialization.  */
       gen_tmpl = most_general_template (DECL_TI_TEMPLATE (t));
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit1.C
index e69de29bb2d..f0ee7c3de23 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 42; }
+
+struct S {
+  explicit(true) S(int);
+  explicit(1 == 0) S(int, int);
+  explicit(fn0()) S(int, int, int);
+  explicit(fn1()) S(int, int, int, int);
+};
+
+struct X {
+  static const bool value = true;
+  static constexpr bool foo () { return 1; }
+};
+
+struct T {
+  explicit(true ? 1 : throw 1) T(int);
+  explicit(true || true ? 1 : throw 1) T(int, int);
+  explicit(X::value) T(int, int, int);
+  explicit(X::foo ()) T(int, int, int, int);
+};
+
+struct W {
+  constexpr operator bool() { return true; };
+};
+
+struct W2 {
+  constexpr operator bool() { return false; };
+};
+
+struct U {
+  explicit(W()) U(int);
+  explicit(W2()) U(int, int);
+};
+
+int
+main ()
+{
+  S s1 = { 1 }; // { dg-error "converting" }
+  S s1x{ 1 };
+  S s2 = { 2, 3 };
+  S s3 = { 4, 5, 6 };
+  S s4 = { 7, 8, 9, 10 }; // { dg-error "converting" }
+  S s4x{ 7, 8, 9, 10 };
+
+  T t1 = { 1 }; // { dg-error "converting" }
+  T t2 = { 1, 2 }; // { dg-error "converting" }
+  T t3 = { 1, 2, 3 }; // { dg-error "converting" }
+  T t4 = { 1, 2, 3, 4 }; // { dg-error "converting" }
+  T t5{ 1 };
+  T t6{ 1, 2 };
+  T t7{ 1, 2, 3 };
+  T t8{ 1, 2, 3, 4 };
+
+  U u1 = { 1 }; // { dg-error "converting" }
+  U u2{ 1 };
+  U u3 = { 1, 2 };
+  U u4 { 1, 2 };
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
index e69de29bb2d..c8701551c9a 100644
--- gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
+++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit10.C
@@ -0,0 +1,32 @@ 
+// P0892R2
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+#include <type_traits>
+
+class A {};
+class B : public A {};
+class C {};
+class D { public: operator C() { return c; }  C c; };
+
+template <typename T1, typename T2>
+struct S {
+  explicit(!std::is_convertible_v<T1, T2>)
+  S(T1, T2) { }
+};
+
+void
+foo ()
+{
+  A a;
+  B b;
+  C c;
+  D d;
+
+  S<int, int> s{ 1, 2 };
+  S<int, int> s2 = { 1, 2 };
+  S<B*, A*> s3 = { &b, &a };
+  S<A*, B*> s4 = { &a, &b }; // { dg-error "converting" }
+  S<B*, C*> s5 = { &b, &c }; // { dg-error "converting" }
+  S<D, C> s6 = { d, c };
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
index e69de29bb2d..ad1bed5b3f0 100644
--- gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
+++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit11.C
@@ -0,0 +1,29 @@ 
+// P0892R2
+// { dg-do compile }
+// { dg-options "-std=c++2a -pedantic" }
+
+template<typename T>
+struct A {
+  explicit A(const T&, ...) noexcept;
+  A(T&&, ...);
+};
+
+int i;
+A a1 = { i, i }; // { dg-error "deduction|cannot" }
+A a2{ i, i };
+A a3{ 0, i };
+A a4 = { 0, i };
+
+template<typename T> A(const T&, const T&) -> A<T&>;
+template<typename T> explicit A(T&&, T&&) -> A<T>;
+
+A a5 = { 0, 1 }; // { dg-error "deduction|ambiguous" }
+A a6{ 0, 1 };
+
+template<typename T>
+struct B {
+  template<typename U> using TA = T;
+  template<typename U> B(U, TA<U>);
+};
+
+B b{(int *)0, (char *)0};
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit2.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit2.C
index e69de29bb2d..7d1748c0f5e 100644
--- gcc/gcc/testsuite/g++.dg/cpp2a/explicit2.C
+++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit2.C
@@ -0,0 +1,25 @@ 
+// P0892R2
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+int foo() { return 42; }
+int g;
+
+struct S {
+  explicit(foo()) S(int); // { dg-error "call to" }
+  explicit(int) S(int, int); // { dg-error "expected" }
+  explicit(false ? 1 : throw 1) S(int, int, int); // { dg-error "not a constant" }
+};
+
+struct S2 {
+  explicit(true) S2();
+  explicit(false) S2(); // { dg-error "cannot be overloaded" }
+};
+
+int
+main ()
+{
+  S s1 = { 1 };
+  S s2 = { 1, 2 }; // { dg-error "could not convert" }
+  S s3 = { 1, 2, 3 };
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit3.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit3.C
index e69de29bb2d..7c495a3640b 100644
--- gcc/gcc/testsuite/g++.dg/cpp2a/explicit3.C
+++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit3.C
@@ -0,0 +1,24 @@ 
+// P0892R2
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+#include <type_traits>
+
+template <typename T1, typename T2>
+struct pair {
+    template <typename U1=T1, typename U2=T2,
+        std::enable_if_t<
+            std::is_constructible_v<T1, U1> &&
+            std::is_constructible_v<T2, U2>
+        , int> = 0>
+    explicit(!std::is_convertible_v<U1, T1> ||
+        !std::is_convertible_v<U2, T2>)
+    constexpr pair(U1&&, U2&&) { }
+};
+
+void
+foo ()
+{
+  pair<int, int> p{1, 2};
+  pair<int, int> p2 = {1, 2};
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit4.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit4.C
index e69de29bb2d..c491bd0808f 100644
--- gcc/gcc/testsuite/g++.dg/cpp2a/explicit4.C
+++ gcc/gcc/testsuite/g++.dg/cpp2a/explicit4.C
@@ -0,0 +1,42 @@ 
+// 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 = 4;
+  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" }
+  S3<double> s14 = { 1 }; // { dg-error "converting" }
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/explicit5.C gcc/gcc/testsuite/g++.dg/cpp2a/explicit5.C
index e69de29bb2d..ebfe412508b 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 42; }
+
+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>{});