mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-10 16:00:55 +08:00
Implement P0892R2, explicit(bool).
* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_explicit_bool. * call.c (add_template_candidate_real): Return if the declaration is explicit and we're only looking for non-converting constructor. * cp-tree.h (lang_decl_fn): Add has_dependent_explicit_spec_p bit. (DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P): New macro. (cp_decl_specifier_seq): Add explicit_specifier field. (build_explicit_specifier, store_explicit_specifier): Declare. * decl.c (grokdeclarator): Call store_explicit_specifier. (build_explicit_specifier): New function. * parser.c (cp_parser_function_specifier_opt) <case RID_EXPLICIT>: Parse C++20 explicit(bool). * pt.c (store_explicit_specifier, lookup_explicit_specifier): New. (tsubst_function_decl): Handle explicit(dependent-expr). * g++.dg/cpp2a/explicit1.C: New test. * g++.dg/cpp2a/explicit10.C: New test. * g++.dg/cpp2a/explicit11.C: New test. * g++.dg/cpp2a/explicit12.C: New test. * g++.dg/cpp2a/explicit13.C: New test. * g++.dg/cpp2a/explicit2.C: New test. * g++.dg/cpp2a/explicit3.C: New test. * g++.dg/cpp2a/explicit4.C: New test. * g++.dg/cpp2a/explicit5.C: New test. * g++.dg/cpp2a/explicit6.C: New test. * g++.dg/cpp2a/explicit7.C: New test. * g++.dg/cpp2a/explicit8.C: New test. * g++.dg/cpp2a/explicit9.C: New test. * testsuite/20_util/any/cons/explicit.cc: Adjust dg-error. * testsuite/20_util/pair/cons/explicit_construct.cc: Likewise. * testsuite/20_util/tuple/cons/explicit_construct.cc: Likewise. From-SVN: r265641
This commit is contained in:
parent
8d42623b37
commit
b5ff4f5c0d
@ -1,3 +1,8 @@
|
||||
2018-10-30 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
Implement P0892R2, explicit(bool).
|
||||
* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_explicit_bool.
|
||||
|
||||
2018-10-29 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* name-hint.h (name_hint::take_deferred): New member function.
|
||||
|
@ -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)
|
||||
|
@ -1,3 +1,19 @@
|
||||
2018-10-30 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
Implement P0892R2, explicit(bool).
|
||||
* call.c (add_template_candidate_real): Return if the declaration is
|
||||
explicit and we're only looking for non-converting constructor.
|
||||
* cp-tree.h (lang_decl_fn): Add has_dependent_explicit_spec_p bit.
|
||||
(DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P): New macro.
|
||||
(cp_decl_specifier_seq): Add explicit_specifier field.
|
||||
(build_explicit_specifier, store_explicit_specifier): Declare.
|
||||
* decl.c (grokdeclarator): Call store_explicit_specifier.
|
||||
(build_explicit_specifier): New function.
|
||||
* parser.c (cp_parser_function_specifier_opt) <case RID_EXPLICIT>:
|
||||
Parse C++20 explicit(bool).
|
||||
* pt.c (store_explicit_specifier, lookup_explicit_specifier): New.
|
||||
(tsubst_function_decl): Handle explicit(dependent-expr).
|
||||
|
||||
2018-10-30 Paolo Carlini <paolo.carlini@oracle.com>
|
||||
|
||||
* decl.c (grokdeclarator): Use declarator->id_loc in diagnostic
|
||||
|
@ -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);
|
||||
|
@ -2587,7 +2587,8 @@ struct GTY(()) lang_decl_fn {
|
||||
unsigned this_thunk_p : 1;
|
||||
unsigned hidden_friend_p : 1;
|
||||
unsigned omp_declare_reduction_p : 1;
|
||||
unsigned spare : 13;
|
||||
unsigned has_dependent_explicit_spec_p : 1;
|
||||
unsigned spare : 12;
|
||||
|
||||
/* 32-bits padding on 64-bit host. */
|
||||
|
||||
@ -3033,6 +3034,12 @@ struct GTY(()) lang_decl {
|
||||
#define DECL_PURE_VIRTUAL_P(NODE) \
|
||||
(LANG_DECL_FN_CHECK (NODE)->pure_virtual)
|
||||
|
||||
/* Nonzero for FUNCTION_DECL means that this member function (either
|
||||
a constructor or a conversion function) has an explicit specifier
|
||||
with a value-dependent expression. */
|
||||
#define DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P(NODE) \
|
||||
(LANG_DECL_FN_CHECK (NODE)->has_dependent_explicit_spec_p)
|
||||
|
||||
/* True (in a FUNCTION_DECL) if NODE is a virtual function that is an
|
||||
invalid overrider for a function from a base class. Once we have
|
||||
complained about an invalid overrider we avoid complaining about it
|
||||
@ -5748,6 +5755,8 @@ struct cp_decl_specifier_seq {
|
||||
/* If non-NULL, a built-in type that the user attempted to redefine
|
||||
to some other type. */
|
||||
tree redefined_builtin_type;
|
||||
/* The explicit-specifier, if any. */
|
||||
tree explicit_specifier;
|
||||
/* The storage class specified -- or sc_none if no storage class was
|
||||
explicitly specified. */
|
||||
cp_storage_class storage_class;
|
||||
@ -6375,6 +6384,7 @@ extern tree cxx_maybe_build_cleanup (tree, tsubst_flags_t);
|
||||
extern bool check_array_designated_initializer (constructor_elt *,
|
||||
unsigned HOST_WIDE_INT);
|
||||
extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
|
||||
extern tree build_explicit_specifier (tree, tsubst_flags_t);
|
||||
|
||||
/* in decl2.c */
|
||||
extern void record_mangling (tree, bool);
|
||||
@ -6772,6 +6782,7 @@ extern bool dguide_name_p (tree);
|
||||
extern bool deduction_guide_p (const_tree);
|
||||
extern bool copy_guide_p (const_tree);
|
||||
extern bool template_guide_p (const_tree);
|
||||
extern void store_explicit_specifier (tree, tree);
|
||||
|
||||
/* in repo.c */
|
||||
extern void init_repo (void);
|
||||
|
@ -12382,6 +12382,9 @@ grokdeclarator (const cp_declarator *declarator,
|
||||
is called a converting constructor. */
|
||||
if (explicitp == 2)
|
||||
DECL_NONCONVERTING_P (decl) = 1;
|
||||
|
||||
if (declspecs->explicit_specifier)
|
||||
store_explicit_specifier (decl, declspecs->explicit_specifier);
|
||||
}
|
||||
else if (!staticp && !dependent_type_p (type)
|
||||
&& !COMPLETE_TYPE_P (complete_type (type))
|
||||
@ -16562,4 +16565,20 @@ require_deduced_type (tree decl, tsubst_flags_t complain)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Create a representation of the explicit-specifier with
|
||||
constant-expression of EXPR. COMPLAIN is as for tsubst. */
|
||||
|
||||
tree
|
||||
build_explicit_specifier (tree expr, tsubst_flags_t complain)
|
||||
{
|
||||
if (processing_template_decl && value_dependent_expression_p (expr))
|
||||
/* Wait for instantiation, tsubst_function_decl will handle it. */
|
||||
return expr;
|
||||
|
||||
expr = build_converted_constant_expr (boolean_type_node, expr, complain);
|
||||
expr = instantiate_non_dependent_expr (expr);
|
||||
expr = cxx_constant_value (expr);
|
||||
return expr;
|
||||
}
|
||||
|
||||
#include "gt-cp-decl.h"
|
||||
|
@ -13897,6 +13897,9 @@ cp_parser_storage_class_specifier_opt (cp_parser* parser)
|
||||
virtual
|
||||
explicit
|
||||
|
||||
C++2A Extension:
|
||||
explicit(constant-expression)
|
||||
|
||||
Returns an IDENTIFIER_NODE corresponding to the keyword used.
|
||||
Updates DECL_SPECS, if it is non-NULL. */
|
||||
|
||||
@ -13923,8 +13926,53 @@ cp_parser_function_specifier_opt (cp_parser* parser,
|
||||
break;
|
||||
|
||||
case RID_EXPLICIT:
|
||||
set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
|
||||
break;
|
||||
{
|
||||
tree id = cp_lexer_consume_token (parser->lexer)->u.value;
|
||||
/* If we see '(', it's C++20 explicit(bool). */
|
||||
tree expr;
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
|
||||
{
|
||||
matching_parens parens;
|
||||
parens.consume_open (parser);
|
||||
|
||||
/* New types are not allowed in an explicit-specifier. */
|
||||
const char *saved_message
|
||||
= parser->type_definition_forbidden_message;
|
||||
parser->type_definition_forbidden_message
|
||||
= G_("types may not be defined in explicit-specifier");
|
||||
|
||||
if (cxx_dialect < cxx2a)
|
||||
pedwarn (token->location, 0,
|
||||
"%<explicit(bool)%> only available with -std=c++2a "
|
||||
"or -std=gnu++2a");
|
||||
|
||||
/* Parse the constant-expression. */
|
||||
expr = cp_parser_constant_expression (parser);
|
||||
|
||||
/* Restore the saved message. */
|
||||
parser->type_definition_forbidden_message = saved_message;
|
||||
parens.require_close (parser);
|
||||
}
|
||||
else
|
||||
/* The explicit-specifier explicit without a constant-expression is
|
||||
equivalent to the explicit-specifier explicit(true). */
|
||||
expr = boolean_true_node;
|
||||
|
||||
/* [dcl.fct.spec]
|
||||
"the constant-expression, if supplied, shall be a contextually
|
||||
converted constant expression of type bool." */
|
||||
expr = build_explicit_specifier (expr, tf_warning_or_error);
|
||||
/* We could evaluate it -- mark the decl as appropriate. */
|
||||
if (expr == boolean_true_node)
|
||||
set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
|
||||
else if (expr == boolean_false_node)
|
||||
/* Don't mark the decl as explicit. */;
|
||||
else if (decl_specs)
|
||||
/* The expression was value-dependent. Remember it so that we can
|
||||
substitute it later. */
|
||||
decl_specs->explicit_specifier = expr;
|
||||
return id;
|
||||
}
|
||||
|
||||
default:
|
||||
return NULL_TREE;
|
||||
|
33
gcc/cp/pt.c
33
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
|
||||
|
@ -1,3 +1,20 @@
|
||||
2018-10-30 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
Implement P0892R2, explicit(bool).
|
||||
* g++.dg/cpp2a/explicit1.C: New test.
|
||||
* g++.dg/cpp2a/explicit10.C: New test.
|
||||
* g++.dg/cpp2a/explicit11.C: New test.
|
||||
* g++.dg/cpp2a/explicit12.C: New test.
|
||||
* g++.dg/cpp2a/explicit13.C: New test.
|
||||
* g++.dg/cpp2a/explicit2.C: New test.
|
||||
* g++.dg/cpp2a/explicit3.C: New test.
|
||||
* g++.dg/cpp2a/explicit4.C: New test.
|
||||
* g++.dg/cpp2a/explicit5.C: New test.
|
||||
* g++.dg/cpp2a/explicit6.C: New test.
|
||||
* g++.dg/cpp2a/explicit7.C: New test.
|
||||
* g++.dg/cpp2a/explicit8.C: New test.
|
||||
* g++.dg/cpp2a/explicit9.C: New test.
|
||||
|
||||
2018-10-30 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
|
||||
PR rtl-optimization/87708
|
||||
|
63
gcc/testsuite/g++.dg/cpp2a/explicit1.C
Normal file
63
gcc/testsuite/g++.dg/cpp2a/explicit1.C
Normal file
@ -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 };
|
||||
}
|
32
gcc/testsuite/g++.dg/cpp2a/explicit10.C
Normal file
32
gcc/testsuite/g++.dg/cpp2a/explicit10.C
Normal file
@ -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 };
|
||||
}
|
29
gcc/testsuite/g++.dg/cpp2a/explicit11.C
Normal file
29
gcc/testsuite/g++.dg/cpp2a/explicit11.C
Normal file
@ -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};
|
23
gcc/testsuite/g++.dg/cpp2a/explicit12.C
Normal file
23
gcc/testsuite/g++.dg/cpp2a/explicit12.C
Normal file
@ -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" }
|
||||
}
|
35
gcc/testsuite/g++.dg/cpp2a/explicit13.C
Normal file
35
gcc/testsuite/g++.dg/cpp2a/explicit13.C
Normal file
@ -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" }
|
||||
}
|
25
gcc/testsuite/g++.dg/cpp2a/explicit2.C
Normal file
25
gcc/testsuite/g++.dg/cpp2a/explicit2.C
Normal file
@ -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 };
|
||||
}
|
24
gcc/testsuite/g++.dg/cpp2a/explicit3.C
Normal file
24
gcc/testsuite/g++.dg/cpp2a/explicit3.C
Normal file
@ -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};
|
||||
}
|
41
gcc/testsuite/g++.dg/cpp2a/explicit4.C
Normal file
41
gcc/testsuite/g++.dg/cpp2a/explicit4.C
Normal file
@ -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" }
|
||||
}
|
71
gcc/testsuite/g++.dg/cpp2a/explicit5.C
Normal file
71
gcc/testsuite/g++.dg/cpp2a/explicit5.C
Normal file
@ -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;
|
||||
}
|
41
gcc/testsuite/g++.dg/cpp2a/explicit6.C
Normal file
41
gcc/testsuite/g++.dg/cpp2a/explicit6.C
Normal file
@ -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};
|
||||
}
|
22
gcc/testsuite/g++.dg/cpp2a/explicit7.C
Normal file
22
gcc/testsuite/g++.dg/cpp2a/explicit7.C
Normal file
@ -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};
|
||||
}
|
24
gcc/testsuite/g++.dg/cpp2a/explicit8.C
Normal file
24
gcc/testsuite/g++.dg/cpp2a/explicit8.C
Normal file
@ -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};
|
||||
}
|
22
gcc/testsuite/g++.dg/cpp2a/explicit9.C
Normal file
22
gcc/testsuite/g++.dg/cpp2a/explicit9.C
Normal file
@ -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};
|
||||
}
|
@ -1,3 +1,10 @@
|
||||
2018-10-30 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
Implement P0892R2, explicit(bool).
|
||||
* testsuite/20_util/any/cons/explicit.cc: Adjust dg-error.
|
||||
* testsuite/20_util/pair/cons/explicit_construct.cc: Likewise.
|
||||
* testsuite/20_util/tuple/cons/explicit_construct.cc: Likewise.
|
||||
|
||||
2018-10-30 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
PR libstdc++/87809
|
||||
|
@ -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" }
|
||||
}
|
||||
|
@ -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" }
|
||||
|
@ -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>{});
|
||||
|
Loading…
x
Reference in New Issue
Block a user