Commit e6d7d618 by Jason Merrill Committed by Jason Merrill

Missing bits from N4268, constant evaluation for all non-type args.

	* call.c (build_converted_constant_expr): Rename from
	build_integral_nontype_arg_conv, handle all types.
	* pt.c (convert_nontype_argument): In C++17 call it for all types.
	Move NOP stripping inside pointer case, don't strip ADDR_EXPR.
	* cvt.c (strip_fnptr_conv): Also strip conversions to the same type.

From-SVN: r249089
parent 8b8b203a
2017-06-09 Jason Merrill <jason@redhat.com> 2017-06-09 Jason Merrill <jason@redhat.com>
Missing bits from N4268, constant evaluation for all non-type args.
* call.c (build_converted_constant_expr): Rename from
build_integral_nontype_arg_conv, handle all types.
* pt.c (convert_nontype_argument): In C++17 call it for all types.
Move NOP stripping inside pointer case, don't strip ADDR_EXPR.
* cvt.c (strip_fnptr_conv): Also strip conversions to the same type.
Overhaul pointer-to-member conversion and template argument handling. Overhaul pointer-to-member conversion and template argument handling.
* call.c (standard_conversion): Avoid creating ck_pmem when the * call.c (standard_conversion): Avoid creating ck_pmem when the
class type is the same. class type is the same.
......
...@@ -4005,15 +4005,16 @@ build_user_type_conversion (tree totype, tree expr, int flags, ...@@ -4005,15 +4005,16 @@ build_user_type_conversion (tree totype, tree expr, int flags,
/* Subroutine of convert_nontype_argument. /* Subroutine of convert_nontype_argument.
EXPR is an argument for a template non-type parameter of integral or EXPR is an expression used in a context that requires a converted
enumeration type. Do any necessary conversions (that are permitted for constant-expression, such as a template non-type parameter. Do any
non-type arguments) to convert it to the parameter type. necessary conversions (that are permitted for converted
constant-expressions) to convert it to the desired type.
If conversion is successful, returns the converted expression; If conversion is successful, returns the converted expression;
otherwise, returns error_mark_node. */ otherwise, returns error_mark_node. */
tree tree
build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain) build_converted_constant_expr (tree type, tree expr, tsubst_flags_t complain)
{ {
conversion *conv; conversion *conv;
void *p; void *p;
...@@ -4023,8 +4024,6 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain) ...@@ -4023,8 +4024,6 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
if (error_operand_p (expr)) if (error_operand_p (expr))
return error_mark_node; return error_mark_node;
gcc_assert (INTEGRAL_OR_ENUMERATION_TYPE_P (type));
/* Get the high-water mark for the CONVERSION_OBSTACK. */ /* Get the high-water mark for the CONVERSION_OBSTACK. */
p = conversion_obstack_alloc (0); p = conversion_obstack_alloc (0);
...@@ -4032,36 +4031,86 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain) ...@@ -4032,36 +4031,86 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
/*c_cast_p=*/false, /*c_cast_p=*/false,
LOOKUP_IMPLICIT, complain); LOOKUP_IMPLICIT, complain);
/* for a non-type template-parameter of integral or /* A converted constant expression of type T is an expression, implicitly
enumeration type, integral promotions (4.5) and integral converted to type T, where the converted expression is a constant
conversions (4.7) are applied. */ expression and the implicit conversion sequence contains only
/* It should be sufficient to check the outermost conversion step, since
there are no qualification conversions to integer type. */ * user-defined conversions,
if (conv) * lvalue-to-rvalue conversions (7.1),
switch (conv->kind) * array-to-pointer conversions (7.2),
{ * function-to-pointer conversions (7.3),
/* A conversion function is OK. If it isn't constexpr, we'll * qualification conversions (7.5),
complain later that the argument isn't constant. */ * integral promotions (7.6),
case ck_user: * integral conversions (7.8) other than narrowing conversions (11.6.4),
/* The lvalue-to-rvalue conversion is OK. */ * null pointer conversions (7.11) from std::nullptr_t,
case ck_rvalue: * null member pointer conversions (7.12) from std::nullptr_t, and
case ck_identity: * function pointer conversions (7.13),
break;
and where the reference binding (if any) binds directly. */
for (conversion *c = conv;
conv && c->kind != ck_identity;
c = next_conversion (c))
{
switch (c->kind)
{
/* A conversion function is OK. If it isn't constexpr, we'll
complain later that the argument isn't constant. */
case ck_user:
/* The lvalue-to-rvalue conversion is OK. */
case ck_rvalue:
/* Array-to-pointer and function-to-pointer. */
case ck_lvalue:
/* Function pointer conversions. */
case ck_fnptr:
/* Qualification conversions. */
case ck_qual:
break;
case ck_std: case ck_ref_bind:
t = next_conversion (conv)->type; if (c->need_temporary_p)
if (INTEGRAL_OR_ENUMERATION_TYPE_P (t)) {
if (complain & tf_error)
error_at (loc, "initializing %qH with %qI in converted "
"constant expression does not bind directly",
type, next_conversion (c)->type);
conv = NULL;
}
break; break;
if (complain & tf_error) case ck_base:
error_at (loc, "conversion from %qH to %qI not considered for " case ck_pmem:
"non-type template argument", t, type); case ck_ptr:
/* fall through. */ case ck_std:
t = next_conversion (c)->type;
if (INTEGRAL_OR_ENUMERATION_TYPE_P (t)
&& INTEGRAL_OR_ENUMERATION_TYPE_P (type))
/* Integral promotion or conversion. */
break;
if (NULLPTR_TYPE_P (t))
/* Conversion from nullptr to pointer or pointer-to-member. */
break;
default: if (complain & tf_error)
conv = NULL; error_at (loc, "conversion from %qH to %qI in a "
break; "converted constant expression", t, type);
} /* fall through. */
default:
conv = NULL;
break;
}
}
/* Avoid confusing convert_nontype_argument by introducing
a redundant conversion to the same reference type. */
if (conv && conv->kind == ck_ref_bind
&& REFERENCE_REF_P (expr))
{
tree ref = TREE_OPERAND (expr, 0);
if (same_type_p (type, TREE_TYPE (ref)))
return ref;
}
if (conv) if (conv)
expr = convert_like (conv, expr, complain); expr = convert_like (conv, expr, complain);
......
...@@ -5907,7 +5907,7 @@ extern bool reference_related_p (tree, tree); ...@@ -5907,7 +5907,7 @@ extern bool reference_related_p (tree, tree);
extern int remaining_arguments (tree); extern int remaining_arguments (tree);
extern tree perform_implicit_conversion (tree, tree, tsubst_flags_t); extern tree perform_implicit_conversion (tree, tree, tsubst_flags_t);
extern tree perform_implicit_conversion_flags (tree, tree, tsubst_flags_t, int); extern tree perform_implicit_conversion_flags (tree, tree, tsubst_flags_t, int);
extern tree build_integral_nontype_arg_conv (tree, tree, tsubst_flags_t); extern tree build_converted_constant_expr (tree, tree, tsubst_flags_t);
extern tree perform_direct_initialization_if_possible (tree, tree, bool, extern tree perform_direct_initialization_if_possible (tree, tree, bool,
tsubst_flags_t); tsubst_flags_t);
extern tree in_charge_arg_for_name (tree); extern tree in_charge_arg_for_name (tree);
......
...@@ -2020,8 +2020,8 @@ fnptr_conv_p (tree to, tree from) ...@@ -2020,8 +2020,8 @@ fnptr_conv_p (tree to, tree from)
|| can_convert_tx_safety (t, f)); || can_convert_tx_safety (t, f));
} }
/* Return FN with any NOP_EXPRs that represent function pointer /* Return FN with any NOP_EXPRs stripped that represent function pointer
conversions stripped. */ conversions or conversions to the same type. */
tree tree
strip_fnptr_conv (tree fn) strip_fnptr_conv (tree fn)
...@@ -2029,7 +2029,10 @@ strip_fnptr_conv (tree fn) ...@@ -2029,7 +2029,10 @@ strip_fnptr_conv (tree fn)
while (TREE_CODE (fn) == NOP_EXPR) while (TREE_CODE (fn) == NOP_EXPR)
{ {
tree op = TREE_OPERAND (fn, 0); tree op = TREE_OPERAND (fn, 0);
if (fnptr_conv_p (TREE_TYPE (fn), TREE_TYPE (op))) tree ft = TREE_TYPE (fn);
tree ot = TREE_TYPE (op);
if (same_type_p (ft, ot)
|| fnptr_conv_p (ft, ot))
fn = op; fn = op;
else else
break; break;
......
...@@ -6430,6 +6430,8 @@ static tree ...@@ -6430,6 +6430,8 @@ static tree
convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
{ {
tree expr_type; tree expr_type;
location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
tree orig_expr = expr;
/* Detect immediately string literals as invalid non-type argument. /* Detect immediately string literals as invalid non-type argument.
This special-case is not needed for correctness (we would easily This special-case is not needed for correctness (we would easily
...@@ -6503,18 +6505,17 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6503,18 +6505,17 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
argument for a parameter of pointer to member type, we just want argument for a parameter of pointer to member type, we just want
to leave it in that form rather than lower it to a to leave it in that form rather than lower it to a
CONSTRUCTOR. */; CONSTRUCTOR. */;
else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)) else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)
/* Constant value checking is done later with type conversion. */; || cxx_dialect >= cxx1z)
else if (cxx_dialect >= cxx1z)
{ {
if (TREE_CODE (type) != REFERENCE_TYPE) /* C++17: A template-argument for a non-type template-parameter shall
expr = maybe_constant_value (expr); be a converted constant expression (8.20) of the type of the
else if (REFERENCE_REF_P (expr)) template-parameter. */
{ expr = build_converted_constant_expr (type, expr, complain);
expr = TREE_OPERAND (expr, 0); if (expr == error_mark_node)
expr = maybe_constant_value (expr); return error_mark_node;
expr = convert_from_reference (expr); expr = maybe_constant_value (expr);
} expr = convert_from_reference (expr);
} }
else if (TYPE_PTR_OR_PTRMEM_P (type)) else if (TYPE_PTR_OR_PTRMEM_P (type))
{ {
...@@ -6558,26 +6559,6 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6558,26 +6559,6 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
} }
} }
/* We could also generate a NOP_EXPR(ADDR_EXPR()) when the
parameter is a pointer to object, through decay and
qualification conversion. Let's strip everything. */
else if (TREE_CODE (expr) == NOP_EXPR && TYPE_PTROBV_P (type))
{
tree probe = expr;
STRIP_NOPS (probe);
if (TREE_CODE (probe) == ADDR_EXPR
&& TYPE_PTR_P (TREE_TYPE (probe)))
{
/* Skip the ADDR_EXPR only if it is part of the decay for
an array. Otherwise, it is part of the original argument
in the source code. */
if (TREE_CODE (TREE_TYPE (TREE_OPERAND (probe, 0))) == ARRAY_TYPE)
probe = TREE_OPERAND (probe, 0);
expr = probe;
expr_type = TREE_TYPE (expr);
}
}
/* [temp.arg.nontype]/5, bullet 1 /* [temp.arg.nontype]/5, bullet 1
For a non-type template-parameter of integral or enumeration type, For a non-type template-parameter of integral or enumeration type,
...@@ -6585,10 +6566,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6585,10 +6566,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
(_conv.integral_) are applied. */ (_conv.integral_) are applied. */
if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)) if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
{ {
tree t = build_integral_nontype_arg_conv (type, expr, complain); if (cxx_dialect < cxx11)
t = maybe_constant_value (t); {
if (t != error_mark_node) tree t = build_converted_constant_expr (type, expr, complain);
expr = t; t = maybe_constant_value (t);
if (t != error_mark_node)
expr = t;
}
if (!same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (expr))) if (!same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (expr)))
return error_mark_node; return error_mark_node;
...@@ -6606,8 +6590,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6606,8 +6590,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
return NULL_TREE; return NULL_TREE;
expr = cxx_constant_value (expr); expr = cxx_constant_value (expr);
if (errorcount > errs || warningcount + werrorcount > warns) if (errorcount > errs || warningcount + werrorcount > warns)
inform (EXPR_LOC_OR_LOC (expr, input_location), inform (loc, "in template argument for type %qT ", type);
"in template argument for type %qT ", type);
if (expr == error_mark_node) if (expr == error_mark_node)
return NULL_TREE; return NULL_TREE;
/* else cxx_constant_value complained but gave us /* else cxx_constant_value complained but gave us
...@@ -6629,6 +6612,23 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6629,6 +6612,23 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
conversion (_conv.array_) are applied. */ conversion (_conv.array_) are applied. */
else if (TYPE_PTROBV_P (type)) else if (TYPE_PTROBV_P (type))
{ {
tree decayed = expr;
/* Look through any NOP_EXPRs around an ADDR_EXPR, whether they come from
decay_conversion or an explicit cast. If it's a problematic cast,
we'll complain about it below. */
if (TREE_CODE (expr) == NOP_EXPR)
{
tree probe = expr;
STRIP_NOPS (probe);
if (TREE_CODE (probe) == ADDR_EXPR
&& TYPE_PTR_P (TREE_TYPE (probe)))
{
expr = probe;
expr_type = TREE_TYPE (expr);
}
}
/* [temp.arg.nontype]/1 (TC1 version, DR 49): /* [temp.arg.nontype]/1 (TC1 version, DR 49):
A template-argument for a non-type, non-template template-parameter A template-argument for a non-type, non-template template-parameter
...@@ -6648,15 +6648,14 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6648,15 +6648,14 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
; ;
else if (cxx_dialect >= cxx11 && integer_zerop (expr)) else if (cxx_dialect >= cxx11 && integer_zerop (expr))
/* Null pointer values are OK in C++11. */; /* Null pointer values are OK in C++11. */;
else if (TREE_CODE (expr) != ADDR_EXPR else if (TREE_CODE (expr) != ADDR_EXPR)
&& TREE_CODE (expr_type) != ARRAY_TYPE)
{ {
if (VAR_P (expr)) if (VAR_P (expr))
{ {
if (complain & tf_error) if (complain & tf_error)
error ("%qD is not a valid template argument " error ("%qD is not a valid template argument "
"because %qD is a variable, not the address of " "because %qD is a variable, not the address of "
"a variable", expr, expr); "a variable", orig_expr, expr);
return NULL_TREE; return NULL_TREE;
} }
if (POINTER_TYPE_P (expr_type)) if (POINTER_TYPE_P (expr_type))
...@@ -6664,7 +6663,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6664,7 +6663,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
if (complain & tf_error) if (complain & tf_error)
error ("%qE is not a valid template argument for %qT " error ("%qE is not a valid template argument for %qT "
"because it is not the address of a variable", "because it is not the address of a variable",
expr, type); orig_expr, type);
return NULL_TREE; return NULL_TREE;
} }
/* Other values, like integer constants, might be valid /* Other values, like integer constants, might be valid
...@@ -6673,15 +6672,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6673,15 +6672,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
} }
else else
{ {
tree decl; tree decl = TREE_OPERAND (expr, 0);
decl = ((TREE_CODE (expr) == ADDR_EXPR)
? TREE_OPERAND (expr, 0) : expr);
if (!VAR_P (decl)) if (!VAR_P (decl))
{ {
if (complain & tf_error) if (complain & tf_error)
error ("%qE is not a valid template argument of type %qT " error ("%qE is not a valid template argument of type %qT "
"because %qE is not a variable", expr, type, decl); "because %qE is not a variable", orig_expr, type, decl);
return NULL_TREE; return NULL_TREE;
} }
else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl)) else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl))
...@@ -6689,7 +6686,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6689,7 +6686,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
if (complain & tf_error) if (complain & tf_error)
error ("%qE is not a valid template argument of type %qT " error ("%qE is not a valid template argument of type %qT "
"because %qD does not have external linkage", "because %qD does not have external linkage",
expr, type, decl); orig_expr, type, decl);
return NULL_TREE; return NULL_TREE;
} }
else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx1z) else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx1z)
...@@ -6697,7 +6694,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6697,7 +6694,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
{ {
if (complain & tf_error) if (complain & tf_error)
error ("%qE is not a valid template argument of type %qT " error ("%qE is not a valid template argument of type %qT "
"because %qD has no linkage", expr, type, decl); "because %qD has no linkage", orig_expr, type, decl);
return NULL_TREE; return NULL_TREE;
} }
/* C++17: For a non-type template-parameter of reference or pointer /* C++17: For a non-type template-parameter of reference or pointer
...@@ -6734,9 +6731,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) ...@@ -6734,9 +6731,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
} }
} }
expr = decay_conversion (expr, complain); expr = decayed;
if (expr == error_mark_node)
return error_mark_node;
expr = perform_qualification_conversions (type, expr); expr = perform_qualification_conversions (type, expr);
if (expr == error_mark_node) if (expr == error_mark_node)
...@@ -23858,7 +23853,7 @@ value_dependent_expression_p (tree expression) ...@@ -23858,7 +23853,7 @@ value_dependent_expression_p (tree expression)
{ {
tree t = TREE_OPERAND (expression, i); tree t = TREE_OPERAND (expression, i);
/* In some cases, some of the operands may be missing.l /* In some cases, some of the operands may be missing.
(For example, in the case of PREDECREMENT_EXPR, the (For example, in the case of PREDECREMENT_EXPR, the
amount to increment by may be missing.) That doesn't amount to increment by may be missing.) That doesn't
make the expression dependent. */ make the expression dependent. */
......
...@@ -10,4 +10,4 @@ struct B ...@@ -10,4 +10,4 @@ struct B
{ }; { };
constexpr A a { }; constexpr A a { };
B<a> b; // { dg-error "template argument|invalid type" } B<a> b; // { dg-error "template argument|converted constant" }
...@@ -11,4 +11,4 @@ template<T N = 0, void (A::*)() = &A::foo<N> > struct B {}; // { dg-error "type| ...@@ -11,4 +11,4 @@ template<T N = 0, void (A::*)() = &A::foo<N> > struct B {}; // { dg-error "type|
B<> b; // { dg-message "non-type" } B<> b; // { dg-message "non-type" }
// { dg-prune-output "could not convert" } // { dg-prune-output "(could not convert|no matches)" }
...@@ -14,7 +14,7 @@ void ...@@ -14,7 +14,7 @@ void
foo () foo ()
{ {
a<int> a1; // OK a<int> a1; // OK
a<int>::b<a,int> b1; // { dg-error "template argument" } a<int>::b<a,int> b1; // { dg-error "template argument|converted constant" }
} }
// { dg-prune-output "invalid type in declaration" } // { dg-prune-output "invalid type in declaration" }
...@@ -17,7 +17,7 @@ template <bool name> ...@@ -17,7 +17,7 @@ template <bool name>
class BUG2 : BUG class BUG2 : BUG
{ {
public: public:
typedef BUG1_5<name> ptr; // { dg-error "could not convert template argument" } typedef BUG1_5<name> ptr; // { dg-error "convert" }
}; };
int main() int main()
......
...@@ -10,4 +10,4 @@ template<int N, void (A::*)() = &A::foo<N> > struct B {}; ...@@ -10,4 +10,4 @@ template<int N, void (A::*)() = &A::foo<N> > struct B {};
B<int> b; // { dg-error "type/value mismatch|expected a constant|invalid type" } B<int> b; // { dg-error "type/value mismatch|expected a constant|invalid type" }
// { dg-prune-output "could not convert" } // { dg-prune-output "(could not convert|no match)" }
...@@ -10,17 +10,31 @@ constexpr char const s3[] = "hi"; // OK since C++11 ...@@ -10,17 +10,31 @@ constexpr char const s3[] = "hi"; // OK since C++11
constexpr char const * f() { return s3; } constexpr char const * f() { return s3; }
using fn_p = char const * (*)();
template <fn_p> struct A { };
constexpr fn_p f2() { return f; }
struct B
{
constexpr B() { }
constexpr operator const char *() { return s3; }
};
int main() int main()
{ {
Message<s1> m1; // OK (all versions) Message<s1> m1; // OK (all versions)
Message<s2> m2; // OK for clang since C++14, for gcc since C++17 Message<s2> m2; // OK for clang since C++14, for gcc since C++17
Message<s3> m3; // OK for clang/gcc since C++11 Message<s3> m3; // OK for clang/gcc since C++11
A<f2()> a1; // { dg-error "" "" { target c++14_down } }
static char const s4[] = "hi"; static char const s4[] = "hi";
static constexpr char const s5[] = "hi"; // OK since C++11 static constexpr char const s5[] = "hi"; // OK since C++11
Message<s4> m4; // { dg-error "no linkage" "" { target c++14_down } } Message<s4> m4; // { dg-error "no linkage" "" { target c++14_down } }
Message<s5> m5; // { dg-error "no linkage" "" { target c++14_down } } Message<s5> m5; // { dg-error "no linkage" "" { target c++14_down } }
Message<f()> m6; // { dg-error "" "" { target c++14_down } } Message<f()> m6; // { dg-error "" "" { target c++14_down } }
Message<B{}> m7; // { dg-error "" "" { target c++14_down } }
char const s8[] = "hi"; char const s8[] = "hi";
Message<s8> m8; // { dg-error "" } Message<s8> m8; // { dg-error "" }
......
...@@ -11,7 +11,7 @@ struct Dummy ...@@ -11,7 +11,7 @@ struct Dummy
template<bool B> template<bool B>
void tester() void tester()
{ {
bar<evil>()(); // { dg-error "constant|template" } bar<evil>()(); // { dg-error "constant|template|convert" }
} }
template<bool B> template<bool B>
struct bar struct bar
......
...@@ -4,4 +4,4 @@ template<char const * const x> class Something { }; ...@@ -4,4 +4,4 @@ template<char const * const x> class Something { };
extern char const xyz; extern char const xyz;
class SomethingElse:public Something<xyz> { }; // { dg-error "xyz. is a variable" } class SomethingElse:public Something<xyz> { }; // { dg-error "" }
...@@ -7,7 +7,7 @@ template<int& i> void doit() { ...@@ -7,7 +7,7 @@ template<int& i> void doit() {
template<const int& i> class X { template<const int& i> class X {
public: public:
void foo() { void foo() {
doit<i>(); // { dg-error "cv-qualification|no matching" } doit<i>(); // { dg-error "qualifi|template arg|no matching" }
} }
}; };
......
...@@ -12,5 +12,5 @@ template<void (A::*)()> void bar(); // { dg-message "note" } ...@@ -12,5 +12,5 @@ template<void (A::*)()> void bar(); // { dg-message "note" }
void baz() void baz()
{ {
bar<&B::foo>(); // { dg-error "not a valid template argument|no match" } bar<&B::foo>(); // { dg-error "template argument|no match" }
} }
...@@ -15,10 +15,8 @@ template <int (D::*fun)() const> int Get(); // { dg-message "note" } ...@@ -15,10 +15,8 @@ template <int (D::*fun)() const> int Get(); // { dg-message "note" }
int main () int main ()
{ {
Get<&B::I>(); // { dg-error "not a valid template argument" "not valid" } Get<&B::I>(); // { dg-error "template argument|converted constant" "not valid" }
// { dg-error "no match" "no match" { target *-*-* } .-1 } // { dg-error "no match" "no match" { target *-*-* } .-1 }
// { dg-message "note" "note" { target *-*-* } .-2 } Get<&D::I>(); // { dg-error "template argument|converted constant" "not valid" }
Get<&D::I>(); // { dg-error "not a valid template argument" "not valid" }
// { dg-error "no match" "no match" { target *-*-* } .-1 } // { dg-error "no match" "no match" { target *-*-* } .-1 }
// { dg-message "note" "note" { target *-*-* } .-2 }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment