call.c (null_ptr_cst_p): Use maybe_constant_value.

* call.c (null_ptr_cst_p): Use maybe_constant_value.
	(set_up_extended_ref_temp): Support constant initialization.
	(initialize_reference): Adjust.
	* class.c (check_bitfield_decl): Use cxx_constant_value.
	* cvt.c (ocp_convert): Don't use integral_constant_value when
	converting to class type.
	* decl.c (finish_case_label): Use maybe_constant_value.
	(build_init_list_var_init): Support constant initialization.
	(check_initializer): Likewise.  Reorganize.
	(cp_finish_decl): Likewise.
	(expand_static_init): Likewise.
	(compute_array_index_type): Use maybe_constant_value.
	Add complain parm.
	(create_array_type_for_decl, grokdeclarator): Pass it.
	(build_enumerator): Use cxx_constant_value.
	* decl2.c (grokfield): Use maybe_constant_init.
	* except.c (check_noexcept_r): Handle constexpr.
	(build_noexcept_spec): Use maybe_constant_value.
	* init.c (expand_default_init): Support constant initialization.
	(build_vec_init): Likewise.
	(constant_value_1): Adjust.
	(build_new_1): Adjust.
	* parser.c (cp_parser_constant_expression): Allow non-integral
	in C++0x mode.
	(cp_parser_direct_declarator): Don't fold yet in C++0x mode.
	(cp_parser_initializer_clause): Toss folded result if non-constant.
	* pt.c (fold_decl_constant_value): Remove.
	(convert_nontype_argument): Use maybe_constant_value.  Give clearer
	error about overflow.
	(tsubst): Move array bounds handling into compute_array_index_type.
	(value_dependent_expression_p): Handle constant CALL_EXPR.
	* semantics.c (finish_static_assert): Use maybe_constant_value.
	(ensure_literal_type_for_constexpr_object): Make sure type is complete.
	(potential_constant_expression): Use maybe_constant_value.
	* tree.c (cast_valid_in_integral_constant_expression_p): Any cast
	is potentially valid in C++0x.
	* typeck2.c (store_init_value): Handle constant init.
	(check_narrowing): Use maybe_constant_value.
	(build_functional_cast): Set TREE_CONSTANT on literal T().
	* cp-tree.h (DECL_INTEGRAL_CONSTANT_VAR_P): Remove.
	(LOOKUP_ALREADY_DIGESTED): New.
	(compute_array_index_type): Adjust prototype.

From-SVN: r166167
This commit is contained in:
Jason Merrill 2010-11-01 21:31:18 -04:00
parent c41095db2f
commit fa2200cbb1
94 changed files with 1919 additions and 261 deletions

View File

@ -1,6 +1,53 @@
2010-11-01 Gabriel Dos Reis <gdr@cse.tamu.edu>
Jason Merrill <jason@redhat.com>
* call.c (null_ptr_cst_p): Use maybe_constant_value.
(set_up_extended_ref_temp): Support constant initialization.
(initialize_reference): Adjust.
* class.c (check_bitfield_decl): Use cxx_constant_value.
* cvt.c (ocp_convert): Don't use integral_constant_value when
converting to class type.
* decl.c (finish_case_label): Use maybe_constant_value.
(build_init_list_var_init): Support constant initialization.
(check_initializer): Likewise. Reorganize.
(cp_finish_decl): Likewise.
(expand_static_init): Likewise.
(compute_array_index_type): Use maybe_constant_value.
Add complain parm.
(create_array_type_for_decl, grokdeclarator): Pass it.
(build_enumerator): Use cxx_constant_value.
* decl2.c (grokfield): Use maybe_constant_init.
* except.c (check_noexcept_r): Handle constexpr.
(build_noexcept_spec): Use maybe_constant_value.
* init.c (expand_default_init): Support constant initialization.
(build_vec_init): Likewise.
(constant_value_1): Adjust.
(build_new_1): Adjust.
* parser.c (cp_parser_constant_expression): Allow non-integral
in C++0x mode.
(cp_parser_direct_declarator): Don't fold yet in C++0x mode.
(cp_parser_initializer_clause): Toss folded result if non-constant.
* pt.c (fold_decl_constant_value): Remove.
(convert_nontype_argument): Use maybe_constant_value. Give clearer
error about overflow.
(tsubst): Move array bounds handling into compute_array_index_type.
(value_dependent_expression_p): Handle constant CALL_EXPR.
(tsubst_decl): Don't set
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P yet.
(tsubst_expr) [DECL_EXPR]: Pass it into cp_finish_decl.
(instantiate_decl): Here too.
* semantics.c (finish_static_assert): Use maybe_constant_value.
(ensure_literal_type_for_constexpr_object): Make sure type is complete.
(potential_constant_expression): Use maybe_constant_value.
* tree.c (cast_valid_in_integral_constant_expression_p): Any cast
is potentially valid in C++0x.
* typeck2.c (store_init_value): Handle constant init.
(check_narrowing): Use maybe_constant_value.
(build_functional_cast): Set TREE_CONSTANT on literal T().
* cp-tree.h (DECL_INTEGRAL_CONSTANT_VAR_P): Remove.
(LOOKUP_ALREADY_DIGESTED): New.
(compute_array_index_type): Adjust prototype.
* semantics.c (constexpr_call): New datatype.
(constexpr_call_table): New global table.
(constexpr_call_hash): New.

View File

@ -466,15 +466,24 @@ null_ptr_cst_p (tree t)
A null pointer constant is an integral constant expression
(_expr.const_) rvalue of integer type that evaluates to zero or
an rvalue of type std::nullptr_t. */
t = integral_constant_value (t);
if (t == null_node
|| NULLPTR_TYPE_P (TREE_TYPE (t)))
if (NULLPTR_TYPE_P (TREE_TYPE (t)))
return true;
if (CP_INTEGRAL_TYPE_P (TREE_TYPE (t)) && integer_zerop (t))
if (CP_INTEGRAL_TYPE_P (TREE_TYPE (t)))
{
STRIP_NOPS (t);
if (!TREE_OVERFLOW (t))
return true;
if (cxx_dialect >= cxx0x)
{
t = fold_non_dependent_expr (t);
t = maybe_constant_value (t);
if (TREE_CONSTANT (t) && integer_zerop (t))
return true;
}
else
{
t = integral_constant_value (t);
STRIP_NOPS (t);
if (integer_zerop (t) && !TREE_OVERFLOW (t))
return true;
}
}
return false;
}
@ -7907,9 +7916,32 @@ set_up_extended_ref_temp (tree decl, tree expr, tree *cleanup, tree *initp)
VAR. */
if (TREE_CODE (expr) != TARGET_EXPR)
expr = get_target_expr (expr);
/* Create the INIT_EXPR that will initialize the temporary
variable. */
init = build2 (INIT_EXPR, type, var, expr);
/* If the initializer is constant, put it in DECL_INITIAL so we get
static initialization and use in constant expressions. */
init = maybe_constant_init (expr);
if (TREE_CONSTANT (init))
{
if (literal_type_p (type) && CP_TYPE_CONST_NON_VOLATILE_P (type))
{
/* 5.19 says that a constant expression can include an
lvalue-rvalue conversion applied to "a glvalue of literal type
that refers to a non-volatile temporary object initialized
with a constant expression". Rather than try to communicate
that this VAR_DECL is a temporary, just mark it constexpr.
Currently this is only useful for initializer_list temporaries,
since reference vars can't appear in constant expressions. */
DECL_DECLARED_CONSTEXPR_P (var) = true;
TREE_CONSTANT (var) = true;
}
DECL_INITIAL (var) = init;
init = NULL_TREE;
}
else
/* Create the INIT_EXPR that will initialize the temporary
variable. */
init = build2 (INIT_EXPR, type, var, expr);
if (at_function_scope_p ())
{
add_decl_expr (var);
@ -8067,7 +8099,8 @@ initialize_reference (tree type, tree expr, tree decl, tree *cleanup,
build_pointer_type (base_conv_type),
/*check_access=*/true,
/*nonnull=*/true, complain);
expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), init, expr);
if (init)
expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), init, expr);
}
else
/* Take the address of EXPR. */

View File

@ -2802,7 +2802,7 @@ check_bitfield_decl (tree field)
STRIP_NOPS (w);
/* detect invalid field size. */
w = integral_constant_value (w);
w = cxx_constant_value (w);
if (TREE_CODE (w) != INTEGER_CST)
{

View File

@ -2207,23 +2207,6 @@ struct GTY((variable_size)) lang_decl {
#define DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P(NODE) \
(TREE_LANG_FLAG_2 (VAR_DECL_CHECK (NODE)))
/* Nonzero for a VAR_DECL that can be used in an integral constant
expression.
[expr.const]
An integral constant-expression can only involve ... const
variables of static or enumeration types initialized with
constant expressions ...
The standard does not require that the expression be non-volatile.
G++ implements the proposed correction in DR 457. */
#define DECL_INTEGRAL_CONSTANT_VAR_P(NODE) \
(TREE_CODE (NODE) == VAR_DECL \
&& CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (NODE)) \
&& INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (NODE)) \
&& DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (NODE))
/* Nonzero if the DECL was initialized in the class definition itself,
rather than outside the class. This is used for both static member
VAR_DECLS, and FUNCTION_DECLS that are defined in the class. */
@ -4235,6 +4218,9 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
another mechanism. Exiting early also avoids problems with trying
to perform argument conversions when the class isn't complete yet. */
#define LOOKUP_SPECULATIVE (LOOKUP_LIST_ONLY << 1)
/* Used in calls to store_init_value to suppress its usual call to
digest_init. */
#define LOOKUP_ALREADY_DIGESTED (LOOKUP_SPECULATIVE << 1)
#define LOOKUP_NAMESPACES_ONLY(F) \
(((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
@ -4826,7 +4812,7 @@ extern tree static_fn_type (tree);
extern void revert_static_member_fn (tree);
extern void fixup_anonymous_aggr (tree);
extern int check_static_variable_definition (tree, tree);
extern tree compute_array_index_type (tree, tree);
extern tree compute_array_index_type (tree, tree, tsubst_flags_t);
extern tree check_default_argument (tree, tree);
typedef int (*walk_namespaces_fn) (tree, void *);
extern int walk_namespaces (walk_namespaces_fn,
@ -5258,6 +5244,7 @@ extern tree ensure_literal_type_for_constexpr_object (tree);
extern tree cxx_constant_value (tree);
extern tree maybe_constant_value (tree);
extern tree maybe_constant_init (tree);
extern bool is_sub_constant_expr (tree);
extern bool reduced_constant_expression_p (tree);
enum {

View File

@ -609,7 +609,10 @@ ocp_convert (tree type, tree expr, int convtype, int flags)
return error_mark_node;
}
e = integral_constant_value (e);
/* FIXME remove when moving to c_fully_fold model. */
/* FIXME do we still need this test? */
if (!CLASS_TYPE_P (type))
e = integral_constant_value (e);
if (error_operand_p (e))
return error_mark_node;

View File

@ -2936,9 +2936,9 @@ finish_case_label (location_t loc, tree low_value, tree high_value)
return error_mark_node;
if (low_value)
low_value = decl_constant_value (low_value);
low_value = cxx_constant_value (low_value);
if (high_value)
high_value = decl_constant_value (high_value);
high_value = cxx_constant_value (high_value);
r = c_add_case_label (loc, switch_stack->cases, cond,
SWITCH_STMT_TYPE (switch_stack->switch_stmt),
@ -4530,7 +4530,8 @@ grok_reference_init (tree decl, tree type, tree init, tree *cleanup)
grok_reference_init. */
static tree
build_init_list_var_init (tree decl, tree type, tree init, tree *cleanup)
build_init_list_var_init (tree decl, tree type, tree init, tree *array_init,
tree *cleanup)
{
tree aggr_init, array, arrtype;
init = perform_implicit_conversion (type, init, tf_warning_or_error);
@ -4538,8 +4539,6 @@ build_init_list_var_init (tree decl, tree type, tree init, tree *cleanup)
return error_mark_node;
aggr_init = TARGET_EXPR_INITIAL (init);
init = build2 (INIT_EXPR, type, decl, init);
array = AGGR_INIT_EXPR_ARG (aggr_init, 1);
arrtype = TREE_TYPE (array);
STRIP_NOPS (array);
@ -4549,12 +4548,10 @@ build_init_list_var_init (tree decl, tree type, tree init, tree *cleanup)
static variable and we don't need to do anything here. */
if (decl && TREE_CODE (array) == TARGET_EXPR)
{
tree subinit;
tree var = set_up_extended_ref_temp (decl, array, cleanup, &subinit);
tree var = set_up_extended_ref_temp (decl, array, cleanup, array_init);
var = build_address (var);
var = convert (arrtype, var);
AGGR_INIT_EXPR_ARG (aggr_init, 1) = var;
init = build2 (COMPOUND_EXPR, TREE_TYPE (init), subinit, init);
}
return init;
}
@ -5250,6 +5247,7 @@ check_initializer (tree decl, tree init, int flags, tree *cleanup)
{
tree type = TREE_TYPE (decl);
tree init_code = NULL;
tree extra_init = NULL_TREE;
tree core_type;
/* Things that are going to be initialized need to have complete
@ -5304,16 +5302,21 @@ check_initializer (tree decl, tree init, int flags, tree *cleanup)
gcc_assert (init != NULL_TREE);
init = NULL_TREE;
}
else if (!DECL_EXTERNAL (decl) && TREE_CODE (type) == REFERENCE_TYPE)
else if (!init && DECL_REALLY_EXTERN (decl))
;
else if (TREE_CODE (type) == REFERENCE_TYPE)
init = grok_reference_init (decl, type, init, cleanup);
else if (init)
else if (init || TYPE_NEEDS_CONSTRUCTING (type))
{
if (!init)
check_for_uninitialized_const_var (decl);
/* Do not reshape constructors of vectors (they don't need to be
reshaped. */
if (BRACE_ENCLOSED_INITIALIZER_P (init))
else if (BRACE_ENCLOSED_INITIALIZER_P (init))
{
if (is_std_init_list (type))
return build_init_list_var_init (decl, type, init, cleanup);
init = build_init_list_var_init (decl, type, init,
&extra_init, cleanup);
else if (TYPE_NON_AGGREGATE_CLASS (type))
{
/* Don't reshape if the class has constructors. */
@ -5340,9 +5343,46 @@ check_initializer (tree decl, tree init, int flags, tree *cleanup)
if (TYPE_NEEDS_CONSTRUCTING (type)
|| (CLASS_TYPE_P (type)
&& !BRACE_ENCLOSED_INITIALIZER_P (init)))
return build_aggr_init_full_exprs (decl, init, flags);
else if (TREE_CODE (init) != TREE_VEC)
&& !(init && BRACE_ENCLOSED_INITIALIZER_P (init))))
{
init_code = build_aggr_init_full_exprs (decl, init, flags);
/* If this is a constexpr initializer, expand_default_init will
have returned an INIT_EXPR rather than a CALL_EXPR. In that
case, pull the initializer back out and pass it down into
store_init_value. */
while (TREE_CODE (init_code) == EXPR_STMT
|| TREE_CODE (init_code) == CONVERT_EXPR)
init_code = TREE_OPERAND (init_code, 0);
if (TREE_CODE (init_code) == INIT_EXPR)
{
init = TREE_OPERAND (init_code, 1);
init_code = NULL_TREE;
/* Don't call digest_init; it's unnecessary and will complain
about aggregate initialization of non-aggregate classes. */
flags |= LOOKUP_ALREADY_DIGESTED;
}
else if (DECL_DECLARED_CONSTEXPR_P (decl))
{
/* Declared constexpr, but no suitable initializer; massage
init appropriately so we can pass it into store_init_value
for the error. */
if (init && BRACE_ENCLOSED_INITIALIZER_P (init))
init = finish_compound_literal (type, init);
else if (CLASS_TYPE_P (type)
&& (!init || TREE_CODE (init) == TREE_LIST))
{
init = build_functional_cast (type, init, tf_none);
if (init != error_mark_node)
TARGET_EXPR_DIRECT_INIT_P (init) = true;
}
init_code = NULL_TREE;
}
else
init = NULL_TREE;
}
if (init && TREE_CODE (init) != TREE_VEC)
{
init_code = store_init_value (decl, init, flags);
if (pedantic && TREE_CODE (type) == ARRAY_TYPE
@ -5354,28 +5394,39 @@ check_initializer (tree decl, tree init, int flags, tree *cleanup)
init = NULL;
}
}
else if (DECL_EXTERNAL (decl))
;
else if (TYPE_P (type) && TYPE_NEEDS_CONSTRUCTING (type))
else
{
check_for_uninitialized_const_var (decl);
return build_aggr_init_full_exprs (decl, init, flags);
}
else if (MAYBE_CLASS_TYPE_P (core_type = strip_array_types (type)))
{
if (CLASSTYPE_READONLY_FIELDS_NEED_INIT (core_type)
|| CLASSTYPE_REF_FIELDS_NEED_INIT (core_type))
if (CLASS_TYPE_P (core_type = strip_array_types (type))
&& (CLASSTYPE_READONLY_FIELDS_NEED_INIT (core_type)
|| CLASSTYPE_REF_FIELDS_NEED_INIT (core_type)))
diagnose_uninitialized_cst_or_ref_member (core_type, /*using_new=*/false,
/*complain=*/true);
check_for_uninitialized_const_var (decl);
}
else
check_for_uninitialized_const_var (decl);
if (init && init != error_mark_node)
init_code = build2 (INIT_EXPR, type, decl, init);
if (extra_init)
init_code = add_stmt_to_compound (extra_init, init_code);
if (init_code && DECL_IN_AGGR_P (decl))
{
static int explained = 0;
if (cxx_dialect < cxx0x)
error ("initializer invalid for static member with constructor");
else
error ("non-constant in-class initialization invalid for static "
"member %qD", decl);
if (!explained)
{
error ("(an out of class initialization is required)");
explained = 1;
}
}
return init_code;
}
@ -5746,7 +5797,22 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
DECL_INITIAL (decl) = NULL_TREE;
}
}
if (init && TREE_CODE (decl) == VAR_DECL)
{
DECL_NONTRIVIALLY_INITIALIZED_P (decl) = 1;
/* FIXME we rely on TREE_CONSTANT below; basing that on
init_const_expr_p is probably wrong for C++0x. */
if (init_const_expr_p)
{
/* Set these flags now for C++98 templates. We'll update the
flags in store_init_value for instantiations and C++0x. */
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = 1;
if (decl_maybe_constant_var_p (decl))
TREE_CONSTANT (decl) = 1;
}
}
if (processing_template_decl)
{
bool type_dependent_p;
@ -5763,22 +5829,16 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
DECL_INITIAL (decl) = NULL_TREE;
}
if (init && init_const_expr_p && TREE_CODE (decl) == VAR_DECL)
{
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = 1;
if (DECL_INTEGRAL_CONSTANT_VAR_P (decl))
TREE_CONSTANT (decl) = 1;
}
/* Generally, initializers in templates are expanded when the
template is instantiated. But, if DECL is an integral
constant static data member, then it can be used in future
integral constant expressions, and its value must be
available. */
template is instantiated. But, if DECL is a variable constant
then it can be used in future constant expressions, so its value
must be available. */
if (!(init
&& DECL_CLASS_SCOPE_P (decl)
&& DECL_INTEGRAL_CONSTANT_VAR_P (decl)
/* We just set TREE_CONSTANT appropriately; see above. */
&& TREE_CONSTANT (decl)
&& !type_dependent_p
/* FIXME non-value-dependent constant expression */
&& !value_dependent_init_p (init)))
{
if (init)
@ -5892,16 +5952,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
error ("Java object %qD not allocated with %<new%>", decl);
init = NULL_TREE;
}
if (init)
{
DECL_NONTRIVIALLY_INITIALIZED_P (decl) = 1;
if (init_const_expr_p)
{
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = 1;
if (DECL_INTEGRAL_CONSTANT_VAR_P (decl))
TREE_CONSTANT (decl) = 1;
}
}
init = check_initializer (decl, init, flags, &cleanup);
/* Thread-local storage cannot be dynamically initialized. */
if (DECL_THREAD_LOCAL_P (decl) && init)
@ -6407,9 +6457,8 @@ expand_static_init (tree decl, tree init)
gcc_assert (TREE_CODE (decl) == VAR_DECL);
gcc_assert (TREE_STATIC (decl));
/* Some variables require no initialization. */
/* Some variables require no dynamic initialization. */
if (!init
&& !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))
&& TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
return;
@ -7417,36 +7466,67 @@ check_static_variable_definition (tree decl, tree type)
name of the thing being declared. */
tree
compute_array_index_type (tree name, tree size)
compute_array_index_type (tree name, tree size, tsubst_flags_t complain)
{
tree type;
tree itype;
tree osize = size;
tree abi_1_itype = NULL_TREE;
if (error_operand_p (size))
return error_mark_node;
type = TREE_TYPE (size);
/* The array bound must be an integer type. */
if (!dependent_type_p (type) && !INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (type))
/* type_dependent_expression_p? */
if (!dependent_type_p (type))
{
if (name)
error ("size of array %qD has non-integral type %qT", name, type);
mark_rvalue_use (size);
if (cxx_dialect < cxx0x && TREE_CODE (size) == NOP_EXPR
&& TREE_SIDE_EFFECTS (size))
/* In C++98, we mark a non-constant array bound with a magic
NOP_EXPR with TREE_SIDE_EFFECTS; don't fold in that case. */;
else
error ("size of array has non-integral type %qT", type);
size = integer_one_node;
type = TREE_TYPE (size);
{
size = fold_non_dependent_expr (size);
if (CLASS_TYPE_P (type)
&& CLASSTYPE_LITERAL_P (type))
{
size = build_expr_type_conversion (WANT_INT, size, true);
if (size == error_mark_node)
return error_mark_node;
type = TREE_TYPE (size);
}
size = maybe_constant_value (size);
}
if (error_operand_p (size))
return error_mark_node;
/* The array bound must be an integer type. */
if (!INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (type))
{
if (!(complain & tf_error))
return error_mark_node;
if (name)
error ("size of array %qD has non-integral type %qT", name, type);
else
error ("size of array has non-integral type %qT", type);
size = integer_one_node;
type = TREE_TYPE (size);
}
}
/* A type is dependent if it is...an array type constructed from any
dependent type or whose size is specified by a constant expression
that is value-dependent. */
/* We can only call value_dependent_expression_p on integral constant
expressions; the parser adds a dummy NOP_EXPR with TREE_SIDE_EFFECTS
set if this isn't one. */
expressions; treat non-constant expressions as dependent, too. */
if (processing_template_decl
&& (dependent_type_p (type)
|| TREE_SIDE_EFFECTS (size) || value_dependent_expression_p (size)))
|| !TREE_CONSTANT (size) || value_dependent_expression_p (size)))
{
/* We cannot do any checking for a SIZE that isn't known to be
constant. Just build the index type and mark that it requires
@ -7467,17 +7547,7 @@ compute_array_index_type (tree name, tree size)
would have, but with TYPE_CANONICAL set to the "right"
value that the current ABI would provide. */
abi_1_itype = build_index_type (build_min (MINUS_EXPR, sizetype,
size, integer_one_node));
/* The size might be the result of a cast. */
STRIP_TYPE_NOPS (size);
size = mark_rvalue_use (size);
/* It might be a const variable or enumeration constant. */
size = integral_constant_value (size);
if (error_operand_p (size))
return error_mark_node;
osize, integer_one_node));
/* Normally, the array-bound will be a constant. */
if (TREE_CODE (size) == INTEGER_CST)
@ -7489,24 +7559,37 @@ compute_array_index_type (tree name, tree size)
/* An array must have a positive number of elements. */
if (INT_CST_LT (size, integer_zero_node))
{
if (!(complain & tf_error))
return error_mark_node;
if (name)
error ("size of array %qD is negative", name);
else
error ("size of array is negative");
size = integer_one_node;
}
/* As an extension we allow zero-sized arrays. We always allow
them in system headers because glibc uses them. */
else if (integer_zerop (size) && !in_system_header)
/* As an extension we allow zero-sized arrays. */
else if (integer_zerop (size))
{
if (name)
if (!(complain & tf_error))
/* We must fail if performing argument deduction (as
indicated by the state of complain), so that
another substitution can be found. */
return error_mark_node;
else if (in_system_header)
/* Allow them in system headers because glibc uses them. */;
else if (name)
pedwarn (input_location, OPT_pedantic, "ISO C++ forbids zero-size array %qD", name);
else
pedwarn (input_location, OPT_pedantic, "ISO C++ forbids zero-size array");
}
}
else if (TREE_CONSTANT (size))
else if (TREE_CONSTANT (size)
/* We don't allow VLAs at non-function scopes, or during
tentative template substitution. */
|| !at_function_scope_p () || !(complain & tf_error))
{
if (!(complain & tf_error))
return error_mark_node;
/* `(int) &fn' is not a valid array bound. */
if (name)
error ("size of array %qD is not an integral constant-expression",
@ -7562,6 +7645,8 @@ compute_array_index_type (tree name, tree size)
else if (TREE_CODE (itype) == INTEGER_CST
&& TREE_OVERFLOW (itype))
{
if (!(complain & tf_error))
return error_mark_node;
error ("overflow in array dimension");
TREE_OVERFLOW (itype) = 0;
}
@ -7673,7 +7758,7 @@ create_array_type_for_decl (tree name, tree type, tree size)
/* Figure out the index type for the array. */
if (size)
itype = compute_array_index_type (name, size);
itype = compute_array_index_type (name, size, tf_warning_or_error);
/* [dcl.array]
T is called the array element type; this type shall not be [...] an
@ -9411,7 +9496,8 @@ grokdeclarator (const cp_declarator *declarator,
if (!staticp && TREE_CODE (type) == ARRAY_TYPE
&& TYPE_DOMAIN (type) == NULL_TREE)
{
tree itype = compute_array_index_type (dname, integer_zero_node);
tree itype = compute_array_index_type (dname, integer_zero_node,
tf_warning_or_error);
type = build_cplus_array_type (TREE_TYPE (type), itype);
}
@ -11732,7 +11818,7 @@ build_enumerator (tree name, tree value, tree enumtype, location_t loc)
/* Validate and default VALUE. */
if (value != NULL_TREE)
{
value = integral_constant_value (value);
value = cxx_constant_value (value);
if (TREE_CODE (value) == INTEGER_CST)
{

View File

@ -923,8 +923,7 @@ grokfield (const cp_declarator *declarator,
{
if (TREE_CODE (init) == CONSTRUCTOR)
init = digest_init (TREE_TYPE (value), init);
else
init = integral_constant_value (init);
init = maybe_constant_init (init);
if (init != error_mark_node && !TREE_CONSTANT (init))
{

View File

@ -1054,15 +1054,20 @@ check_noexcept_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
STRIP_NOPS (fn);
if (TREE_CODE (fn) == ADDR_EXPR)
fn = TREE_OPERAND (fn, 0);
if (TREE_CODE (fn) == FUNCTION_DECL)
{
/* We do use TREE_NOTHROW for ABI internals like __dynamic_cast,
and for C library functions known not to throw. */
fn = TREE_OPERAND (fn, 0);
if (TREE_CODE (fn) == FUNCTION_DECL
&& DECL_EXTERN_C_P (fn)
if (DECL_EXTERN_C_P (fn)
&& (DECL_ARTIFICIAL (fn)
|| nothrow_libfn_p (fn)))
return TREE_NOTHROW (fn) ? NULL_TREE : fn;
/* A call to a constexpr function is noexcept if the call
is a constant expression. */
if (DECL_DECLARED_CONSTEXPR_P (fn)
&& is_sub_constant_expr (t))
return NULL_TREE;
}
if (!TYPE_NOTHROW_P (type))
return fn;
@ -1195,9 +1200,15 @@ type_throw_all_p (const_tree type)
tree
build_noexcept_spec (tree expr, int complain)
{
expr = perform_implicit_conversion_flags (boolean_type_node, expr,
complain,
LOOKUP_NORMAL);
/* This isn't part of the signature, so don't bother trying to evaluate
it until instantiation. */
if (!processing_template_decl)
{
expr = cxx_constant_value (expr);
expr = perform_implicit_conversion_flags (boolean_type_node, expr,
complain,
LOOKUP_NORMAL);
}
if (expr == boolean_true_node)
return noexcept_true_spec;
else if (expr == boolean_false_node)

View File

@ -1440,8 +1440,20 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
if (parms != NULL)
release_tree_vector (parms);
if (exp == true_exp && TREE_CODE (rval) == CALL_EXPR)
{
tree fn = get_callee_fndecl (rval);
if (DECL_DECLARED_CONSTEXPR_P (fn))
{
tree e = maybe_constant_value (rval);
if (TREE_CONSTANT (e))
rval = build2 (INIT_EXPR, type, exp, e);
}
}
/* FIXME put back convert_to_void? */
if (TREE_SIDE_EFFECTS (rval))
finish_expr_stmt (convert_to_void (rval, ICV_CAST, complain));
finish_expr_stmt (rval);
}
/* This function is responsible for initializing EXP with INIT
@ -1708,36 +1720,18 @@ constant_value_1 (tree decl, bool integral_p)
{
while (TREE_CODE (decl) == CONST_DECL
|| (integral_p
? DECL_INTEGRAL_CONSTANT_VAR_P (decl)
? decl_constant_var_p (decl)
: (TREE_CODE (decl) == VAR_DECL
&& CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (decl)))))
{
tree init;
/* Static data members in template classes may have
non-dependent initializers. References to such non-static
data members are not value-dependent, so we must retrieve the
initializer here. The DECL_INITIAL will have the right type,
but will not have been folded because that would prevent us
from performing all appropriate semantic checks at
instantiation time. */
if (DECL_CLASS_SCOPE_P (decl)
&& CLASSTYPE_TEMPLATE_INFO (DECL_CONTEXT (decl))
&& uses_template_parms (CLASSTYPE_TI_ARGS
(DECL_CONTEXT (decl))))
{
++processing_template_decl;
init = fold_non_dependent_expr (DECL_INITIAL (decl));
--processing_template_decl;
}
else
{
/* If DECL is a static data member in a template
specialization, we must instantiate it here. The
initializer for the static data member is not processed
until needed; we need it now. */
mark_used (decl);
init = DECL_INITIAL (decl);
}
/* If DECL is a static data member in a template
specialization, we must instantiate it here. The
initializer for the static data member is not processed
until needed; we need it now. */
mark_used (decl);
mark_rvalue_use (decl);
init = DECL_INITIAL (decl);
if (init == error_mark_node)
{
if (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
@ -1758,8 +1752,9 @@ constant_value_1 (tree decl, bool integral_p)
init = TREE_VALUE (init);
if (!init
|| !TREE_TYPE (init)
|| uses_template_parms (init)
|| (integral_p
? !INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (init))
? false
: (!TREE_CONSTANT (init)
/* Do not return an aggregate constant (of which
string literals are a special case), as we do not
@ -2302,7 +2297,7 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
tree arraytype, domain;
vecinit = VEC_index (tree, *init, 0);
if (TREE_CONSTANT (nelts))
domain = compute_array_index_type (NULL_TREE, nelts);
domain = compute_array_index_type (NULL_TREE, nelts, complain);
else
{
domain = NULL_TREE;
@ -2878,6 +2873,8 @@ build_vec_init (tree base, tree maxindex, tree init,
tree try_block = NULL_TREE;
int num_initialized_elts = 0;
bool is_global;
tree const_init = NULL_TREE;
tree obase = base;
bool xvalue = false;
if (TREE_CODE (atype) == ARRAY_TYPE && TYPE_DOMAIN (atype))
@ -2986,26 +2983,75 @@ build_vec_init (tree base, tree maxindex, tree init,
try_block = begin_try_block ();
}
/* Maybe pull out constant value when from_array? */
if (init != NULL_TREE && TREE_CODE (init) == CONSTRUCTOR)
{
/* Do non-default initialization of non-trivial arrays resulting from
brace-enclosed initializers. */
unsigned HOST_WIDE_INT idx;
tree elt;
tree field, elt;
/* Should we try to create a constant initializer? */
bool try_const = (literal_type_p (inner_elt_type)
|| TYPE_HAS_CONSTEXPR_CTOR (inner_elt_type));
bool saw_non_const = false;
bool saw_const = false;
/* If we're initializing a static array, we want to do static
initialization of any elements with constant initializers even if
some are non-constant. */
bool do_static_init = (DECL_P (obase) && TREE_STATIC (obase));
VEC(constructor_elt,gc) *new_vec;
from_array = 0;
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (init), idx, elt)
if (try_const)
new_vec = VEC_alloc (constructor_elt, gc, CONSTRUCTOR_NELTS (init));
else
new_vec = NULL;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, field, elt)
{
tree baseref = build1 (INDIRECT_REF, type, base);
tree one_init;
num_initialized_elts++;
current_stmt_tree ()->stmts_are_full_exprs_p = 1;
if (MAYBE_CLASS_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
finish_expr_stmt (build_aggr_init (baseref, elt, 0, complain));
one_init = build_aggr_init (baseref, elt, 0, complain);
else
finish_expr_stmt (cp_build_modify_expr (baseref, NOP_EXPR,
elt, complain));
one_init = cp_build_modify_expr (baseref, NOP_EXPR,
elt, complain);
if (try_const)
{
tree e = one_init;
if (TREE_CODE (e) == EXPR_STMT)
e = TREE_OPERAND (e, 0);
if (TREE_CODE (e) == CONVERT_EXPR
&& VOID_TYPE_P (TREE_TYPE (e)))
e = TREE_OPERAND (e, 0);
e = maybe_constant_init (e);
if (reduced_constant_expression_p (e))
{
CONSTRUCTOR_APPEND_ELT (new_vec, field, e);
if (do_static_init)
one_init = NULL_TREE;
else
one_init = build2 (INIT_EXPR, type, baseref, e);
saw_const = true;
}
else
{
if (do_static_init)
CONSTRUCTOR_APPEND_ELT (new_vec, field,
build_zero_init (TREE_TYPE (e),
NULL_TREE, true));
saw_non_const = true;
}
}
if (one_init)
finish_expr_stmt (one_init);
current_stmt_tree ()->stmts_are_full_exprs_p = 0;
finish_expr_stmt (cp_build_unary_op (PREINCREMENT_EXPR, base, 0,
@ -3014,6 +3060,16 @@ build_vec_init (tree base, tree maxindex, tree init,
complain));
}
if (try_const)
{
if (!saw_non_const)
const_init = build_constructor (atype, new_vec);
else if (do_static_init && saw_const)
DECL_INITIAL (obase) = build_constructor (atype, new_vec);
else
VEC_free (constructor_elt, gc, new_vec);
}
/* Clear out INIT so that we don't get confused below. */
init = NULL_TREE;
}
@ -3161,6 +3217,9 @@ build_vec_init (tree base, tree maxindex, tree init,
}
current_stmt_tree ()->stmts_are_full_exprs_p = destroy_temps;
if (const_init)
return build2 (INIT_EXPR, atype, obase, const_init);
return stmt_expr;
}

View File

@ -7176,7 +7176,8 @@ cp_parser_constant_expression (cp_parser* parser,
saved_non_integral_constant_expression_p = parser->non_integral_constant_expression_p;
/* We are now parsing a constant-expression. */
parser->integral_constant_expression_p = true;
parser->allow_non_integral_constant_expression_p = allow_non_constant_p;
parser->allow_non_integral_constant_expression_p
= (allow_non_constant_p || cxx_dialect >= cxx0x);
parser->non_integral_constant_expression_p = false;
/* Although the grammar says "conditional-expression", we parse an
"assignment-expression", which also permits "throw-expression"
@ -7195,7 +7196,8 @@ cp_parser_constant_expression (cp_parser* parser,
= saved_allow_non_integral_constant_expression_p;
if (allow_non_constant_p)
*non_constant_p = parser->non_integral_constant_expression_p;
else if (parser->non_integral_constant_expression_p)
else if (parser->non_integral_constant_expression_p
&& cxx_dialect < cxx0x)
expression = error_mark_node;
parser->non_integral_constant_expression_p
= saved_non_integral_constant_expression_p;
@ -14975,8 +14977,8 @@ cp_parser_direct_declarator (cp_parser* parser,
= cp_parser_constant_expression (parser,
/*allow_non_constant=*/true,
&non_constant_p);
if (!non_constant_p)
bounds = fold_non_dependent_expr (bounds);
if (!non_constant_p || cxx_dialect >= cxx0x)
/* OK */;
/* Normally, the array bound must be an integral constant
expression. However, as an extension, we allow VLAs
in function scopes as long as they aren't part of a
@ -16408,7 +16410,15 @@ cp_parser_initializer_clause (cp_parser* parser, bool* non_constant_p)
/*allow_non_constant_p=*/true,
non_constant_p);
if (!*non_constant_p)
initializer = fold_non_dependent_expr (initializer);
{
/* We only want to fold if this is really a constant
expression. FIXME Actually, we don't want to fold here, but in
cp_finish_decl. */
tree folded = fold_non_dependent_expr (initializer);
folded = maybe_constant_value (folded);
if (TREE_CONSTANT (folded))
initializer = folded;
}
}
else
initializer = cp_parser_braced_list (parser, non_constant_p);

View File

@ -4845,29 +4845,6 @@ fold_non_dependent_expr (tree expr)
return fold_non_dependent_expr_sfinae (expr, tf_error);
}
/* EXPR is an expression which is used in a constant-expression context.
For instance, it could be a VAR_DECL with a constant initializer.
Extract the innermost constant expression.
This is basically a more powerful version of
integral_constant_value, which can be used also in templates where
initializers can maintain a syntactic rather than semantic form
(even if they are non-dependent, for access-checking purposes). */
static tree
fold_decl_constant_value (tree expr)
{
tree const_expr = expr;
do
{
expr = fold_non_dependent_expr (const_expr);
const_expr = integral_constant_value (expr);
}
while (expr != const_expr);
return expr;
}
/* Subroutine of convert_nontype_argument. Converts EXPR to TYPE, which
must be a function or a pointer-to-function type, as specified
in [temp.arg.nontype]: disambiguate EXPR if it is an overload set,
@ -5069,23 +5046,23 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
{
tree t = build_integral_nontype_arg_conv (type, expr, complain);
t = fold_decl_constant_value (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)))
return error_mark_node;
/* Conversion was allowed: fold it to a bare integer constant. */
expr = fold (expr);
/* Notice that there are constant expressions like '4 % 0' which
do not fold into integer constants. */
if (TREE_CODE (expr) != INTEGER_CST)
{
if (complain & tf_error)
error ("%qE is not a valid template argument for type %qT "
"because it is a non-constant expression", expr, type);
{
error ("%qE is not a valid template argument for type %qT "
"because it is a non-constant expression", expr, type);
cxx_constant_value (expr);
}
return NULL_TREE;
}
}
@ -9637,8 +9614,9 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
RETURN (error_mark_node);
}
type = complete_type (type);
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (r)
= DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (t);
/* Wait until cp_finish_decl to set this again, to handle
circular dependency (template/instantiate6.C). */
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (r) = 0;
type = check_var_type (DECL_NAME (r), type);
if (DECL_HAS_VALUE_EXPR_P (t))
@ -10125,9 +10103,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
&& !TREE_TYPE (max))
TREE_TYPE (max) = TREE_TYPE (TREE_OPERAND (max, 0));
max = mark_rvalue_use (max);
max = fold_decl_constant_value (max);
/* If we're in a partial instantiation, preserve the magic NOP_EXPR
with TREE_SIDE_EFFECTS that indicates this is not an integral
constant expression. */
@ -10138,38 +10113,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
TREE_SIDE_EFFECTS (max) = 1;
}
if (TREE_CODE (max) != INTEGER_CST
&& !at_function_scope_p ()
&& !TREE_SIDE_EFFECTS (max)
&& !value_dependent_expression_p (max))
{
if (complain & tf_error)
error ("array bound is not an integer constant");
return error_mark_node;
}
/* [temp.deduct]
Type deduction may fail for any of the following
reasons:
Attempting to create an array with a size that is
zero or negative. */
if (integer_zerop (max) && !(complain & tf_error))
/* We must fail if performing argument deduction (as
indicated by the state of complain), so that
another substitution can be found. */
return error_mark_node;
else if (TREE_CODE (max) == INTEGER_CST
&& INT_CST_LT (max, integer_zero_node))
{
if (complain & tf_error)
error ("creating array with negative size (%qE)", max);
return error_mark_node;
}
return compute_array_index_type (NULL_TREE, max);
return compute_array_index_type (NULL_TREE, max, complain);
}
case TEMPLATE_TYPE_PARM:
@ -11658,10 +11602,10 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
case DECL_EXPR:
{
tree decl;
tree decl, pattern_decl;
tree init;
decl = DECL_EXPR_DECL (t);
pattern_decl = decl = DECL_EXPR_DECL (t);
if (TREE_CODE (decl) == LABEL_DECL)
finish_label_decl (DECL_NAME (decl));
else if (TREE_CODE (decl) == USING_DECL)
@ -11698,6 +11642,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
finish_anon_union (decl);
else
{
int const_init = false;
maybe_push_decl (decl);
if (TREE_CODE (decl) == VAR_DECL
&& DECL_PRETTY_FUNCTION_P (decl))
@ -11730,7 +11675,10 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
init = t;
}
cp_finish_decl (decl, init, false, NULL_TREE, 0);
if (TREE_CODE (decl) == VAR_DECL)
const_init = (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P
(pattern_decl));
cp_finish_decl (decl, init, const_init, NULL_TREE, 0);
}
}
}
@ -16894,6 +16842,7 @@ instantiate_decl (tree d, int defer_ok,
{
tree ns;
tree init;
bool const_init = false;
ns = decl_namespace_context (d);
push_nested_namespace (ns);
@ -16902,7 +16851,11 @@ instantiate_decl (tree d, int defer_ok,
args,
tf_warning_or_error, NULL_TREE,
/*integral_constant_expression_p=*/false);
cp_finish_decl (d, init, /*init_const_expr_p=*/false,
/* Make sure the initializer is still constant, in case of
circular dependency (template/instantiate6.C). */
const_init
= DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (code_pattern);
cp_finish_decl (d, init, /*init_const_expr_p=*/const_init,
/*asmspec_tree=*/NULL_TREE,
LOOKUP_ONLYCONVERTING);
pop_nested_class ();
@ -16973,6 +16926,7 @@ instantiate_decl (tree d, int defer_ok,
if (TREE_CODE (d) == VAR_DECL)
{
tree init;
bool const_init = false;
/* Clear out DECL_RTL; whatever was there before may not be right
since we've reset the type of the declaration. */
@ -16980,7 +16934,8 @@ instantiate_decl (tree d, int defer_ok,
DECL_IN_AGGR_P (d) = 0;
/* The initializer is placed in DECL_INITIAL by
regenerate_decl_from_template. Pull it out so that
regenerate_decl_from_template so we don't need to
push/pop_access_scope again here. Pull it out so that
cp_finish_decl can process it. */
init = DECL_INITIAL (d);
DECL_INITIAL (d) = NULL_TREE;
@ -16993,7 +16948,8 @@ instantiate_decl (tree d, int defer_ok,
/* Enter the scope of D so that access-checking works correctly. */
push_nested_class (DECL_CONTEXT (d));
cp_finish_decl (d, init, false, NULL_TREE, 0);
const_init = DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (code_pattern);
cp_finish_decl (d, init, const_init, NULL_TREE, 0);
pop_nested_class ();
}
else if (TREE_CODE (d) == FUNCTION_DECL)
@ -17633,6 +17589,23 @@ dependent_scope_p (tree scope)
[temp.dep.constexpr]. EXPRESSION is already known to be a constant
expression. */
/* FIXME this predicate is not appropriate for general expressions; the
predicates we want instead are "valid constant expression, value
dependent or not?", "really constant expression, not value dependent?"
and "instantiation-dependent?". Try to integrate with
potential_constant_expression?
fold_non_dependent_expr: fold if constant and not type-dependent and not value-dependent.
(what about instantiation-dependent constant-expressions?)
is_late_template_attribute: defer if instantiation-dependent.
compute_array_index_type: proceed if constant and not t- or v-dependent
if instantiation-dependent, need to remember full expression
uses_template_parms: FIXME - need to audit callers
tsubst_decl [function_decl]: Why is this using value_dependent_expression_p?
dependent_type_p [array_type]: dependent if index type is dependent
(or non-constant?)
static_assert - instantiation-dependent */
bool
value_dependent_expression_p (tree expression)
{
@ -17689,7 +17662,8 @@ value_dependent_expression_p (tree expression)
/* If there are no operands, it must be an expression such
as "int()". This should not happen for aggregate types
because it would form non-constant expressions. */
gcc_assert (INTEGRAL_OR_ENUMERATION_TYPE_P (type));
gcc_assert (cxx_dialect >= cxx0x
|| INTEGRAL_OR_ENUMERATION_TYPE_P (type));
return false;
}
@ -17733,12 +17707,6 @@ value_dependent_expression_p (tree expression)
return (value_dependent_expression_p (TREE_OPERAND (expression, 0))
|| value_dependent_expression_p (TREE_OPERAND (expression, 1)));
case CALL_EXPR:
/* A CALL_EXPR may appear in a constant expression if it is a
call to a builtin function, e.g., __builtin_constant_p. All
such calls are value-dependent. */
return true;
case NONTYPE_ARGUMENT_PACK:
/* A NONTYPE_ARGUMENT_PACK is value-dependent if any packed argument
is value-dependent. */
@ -17771,6 +17739,30 @@ value_dependent_expression_p (tree expression)
|| has_value_dependent_address (op));
}
case CALL_EXPR:
{
tree fn = get_callee_fndecl (expression);
int i, nargs;
if (!fn && value_dependent_expression_p (CALL_EXPR_FN (expression)))
return true;
nargs = call_expr_nargs (expression);
for (i = 0; i < nargs; ++i)
{
tree op = CALL_EXPR_ARG (expression, i);
/* In a call to a constexpr member function, look through the
implicit ADDR_EXPR on the object argument so that it doesn't
cause the call to be considered value-dependent. We also
look through it in potential_constant_expression. */
if (i == 0 && fn && DECL_DECLARED_CONSTEXPR_P (fn)
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
&& TREE_CODE (op) == ADDR_EXPR)
op = TREE_OPERAND (op, 0);
if (value_dependent_expression_p (op))
return true;
}
return false;
}
default:
/* A constant expression is value-dependent if any subexpression is
value-dependent. */

View File

@ -328,6 +328,8 @@ build_typeid (tree exp)
if (processing_template_decl)
return build_min (TYPEID_EXPR, const_type_info_type_node, exp);
/* FIXME when integrating with c_fully_fold, mark
resolves_to_fixed_type_p case as a non-constant expression. */
if (TREE_CODE (exp) == INDIRECT_REF
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == POINTER_TYPE
&& TYPE_POLYMORPHIC_P (TREE_TYPE (exp))

View File

@ -4618,6 +4618,7 @@ finish_static_assert (tree condition, tree message, location_t location,
/* Fold the expression and convert it to a boolean value. */
condition = fold_non_dependent_expr (condition);
condition = cp_convert (boolean_type_node, condition);
condition = maybe_constant_value (condition);
if (TREE_CODE (condition) == INTEGER_CST && !integer_zerop (condition))
/* Do nothing; the condition is satisfied. */
@ -4632,7 +4633,10 @@ finish_static_assert (tree condition, tree message, location_t location,
/* Report the error. */
error ("static assertion failed: %E", message);
else if (condition && condition != error_mark_node)
error ("non-constant condition for static assertion");
{
error ("non-constant condition for static assertion");
cxx_constant_value (condition);
}
input_location = saved_loc;
}
}
@ -5273,7 +5277,9 @@ ensure_literal_type_for_constexpr_object (tree decl)
{
tree type = TREE_TYPE (decl);
if (TREE_CODE (decl) == VAR_DECL && DECL_DECLARED_CONSTEXPR_P (decl)
&& !processing_template_decl && !literal_type_p (type))
&& !processing_template_decl
/* The call to complete_type is just for initializer_list. */
&& !literal_type_p (complete_type (type)))
{
error ("the type %qT of constexpr variable %qD is not literal",
type, decl);
@ -6837,6 +6843,17 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant)
return r;
}
/* Returns true if T is a valid subexpression of a constant expression,
even if it isn't itself a constant expression. */
bool
is_sub_constant_expr (tree t)
{
bool non_constant_p = false;
cxx_eval_constant_expression (NULL, t, true, false, &non_constant_p);
return !non_constant_p;
}
/* If T represents a constant expression returns its reduced value.
Otherwise return error_mark_node. If T is dependent, then
return NULL. */
@ -7257,7 +7274,7 @@ potential_constant_expression (tree t, tsubst_flags_t flags)
case TRUNC_MOD_EXPR:
case CEIL_MOD_EXPR:
case ROUND_MOD_EXPR:
if (integer_zerop (decl_constant_value (TREE_OPERAND (t, 1))))
if (integer_zerop (maybe_constant_value (TREE_OPERAND (t, 1))))
return false;
else
goto binary;

View File

@ -3235,6 +3235,7 @@ bool
cast_valid_in_integral_constant_expression_p (tree type)
{
return (INTEGRAL_OR_ENUMERATION_TYPE_P (type)
|| cxx_dialect >= cxx0x
|| dependent_type_p (type)
|| type == error_mark_node);
}

View File

@ -1930,6 +1930,9 @@ decay_conversion (tree exp)
return error_mark_node;
}
/* FIXME remove? at least need to remember that this isn't really a
constant expression if EXP isn't decl_constant_var_p, like with
C_MAYBE_CONST_EXPR. */
exp = decl_constant_value (exp);
if (error_operand_p (exp))
return error_mark_node;

View File

@ -714,9 +714,6 @@ store_init_value (tree decl, tree init, int flags)
if (MAYBE_CLASS_TYPE_P (type))
{
gcc_assert (!type_has_nontrivial_copy_init (type)
|| TREE_CODE (init) == CONSTRUCTOR);
if (TREE_CODE (init) == TREE_LIST)
{
error ("constructor syntax used, but no constructor declared "
@ -743,8 +740,32 @@ store_init_value (tree decl, tree init, int flags)
/* End of special C++ code. */
/* Digest the specified initializer into an expression. */
value = digest_init_flags (type, init, flags);
if (flags & LOOKUP_ALREADY_DIGESTED)
value = init;
else
/* Digest the specified initializer into an expression. */
value = digest_init_flags (type, init, flags);
/* In C++0x constant expression is a semantic, not syntactic, property.
In C++98, make sure that what we thought was a constant expression at
template definition time is still constant. */
if ((cxx_dialect >= cxx0x
|| DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
&& (decl_maybe_constant_var_p (decl)
|| TREE_STATIC (decl)))
{
bool const_init;
value = fold_non_dependent_expr (value);
value = maybe_constant_init (value);
if (DECL_DECLARED_CONSTEXPR_P (decl))
/* Diagnose a non-constant initializer for constexpr. */
value = cxx_constant_value (value);
const_init = (reduced_constant_expression_p (value)
|| error_operand_p (value));
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = const_init;
TREE_CONSTANT (decl) = const_init && decl_maybe_constant_var_p (decl);
}
/* If the initializer is not a constant, fill in DECL_INITIAL with
the bits that are constant, and then return an expression that
will perform the dynamic initialization. */
@ -769,8 +790,7 @@ check_narrowing (tree type, tree init)
bool ok = true;
REAL_VALUE_TYPE d;
if (DECL_P (init))
init = decl_constant_value (init);
init = maybe_constant_value (init);
if (TREE_CODE (type) == INTEGER_TYPE
&& TREE_CODE (ftype) == REAL_TYPE)
@ -1632,7 +1652,11 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
&& !TYPE_HAS_USER_CONSTRUCTOR (type))
{
exp = build_value_init (type, complain);
return get_target_expr (exp);
exp = get_target_expr (exp);
/* FIXME this is wrong */
if (literal_type_p (type))
TREE_CONSTANT (exp) = true;
return exp;
}
/* Call the constructor. */

View File

@ -1,4 +1,82 @@
2010-11-01 Jason Merrill <jason@redhat.com>
Benjamin Kosnik <bkoz@redhat.com>
* g++.dg/cpp0x/constexpr-array-ptr.C: New.
* g++.dg/cpp0x/constexpr-array-ptr2.C: New.
* g++.dg/cpp0x/constexpr-array-ptr3.C: New.
* g++.dg/cpp0x/constexpr-array-ptr4.C: New.
* g++.dg/cpp0x/constexpr-array-ptr5.C: New.
* g++.dg/cpp0x/constexpr-array-ptr6.C: New.
* g++.dg/cpp0x/constexpr-array.C: New.
* g++.dg/cpp0x/constexpr-base.C: New.
* g++.dg/cpp0x/constexpr-complex.C: New.
* g++.dg/cpp0x/constexpr-data1.C: New.
* g++.dg/cpp0x/constexpr-data2.C: New.
* g++.dg/cpp0x/constexpr-deref.C: New.
* g++.dg/cpp0x/constexpr-diag1.C: New.
* g++.dg/cpp0x/constexpr-eh-spec.C: New.
* g++.dg/cpp0x/constexpr-ellipsis.C: New.
* g++.dg/cpp0x/constexpr-ellipsis2.C: New.
* g++.dg/cpp0x/constexpr-empty.C: New.
* g++.dg/cpp0x/constexpr-empty2.C: New.
* g++.dg/cpp0x/constexpr-empty3.C: New.
* g++.dg/cpp0x/constexpr-empty4.C: New.
* g++.dg/cpp0x/constexpr-empty5.C: New.
* g++.dg/cpp0x/constexpr-ex1.C: New.
* g++.dg/cpp0x/constexpr-ex2.C: New.
* g++.dg/cpp0x/constexpr-ex3.C: New.
* g++.dg/cpp0x/constexpr-ex4.C: New.
* g++.dg/cpp0x/constexpr-explicit-inst.C: New.
* g++.dg/cpp0x/constexpr-fnptr.C: New.
* g++.dg/cpp0x/constexpr-function1.C: New.
* g++.dg/cpp0x/constexpr-function2.C: New.
* g++.dg/cpp0x/constexpr-function3.C: New.
* g++.dg/cpp0x/constexpr-ice.C: New.
* g++.dg/cpp0x/constexpr-initlist.C: New.
* g++.dg/cpp0x/constexpr-neg1.C: New.
* g++.dg/cpp0x/constexpr-noexcept.C: New.
* g++.dg/cpp0x/constexpr-noexcept2.C: New.
* g++.dg/cpp0x/constexpr-noexcept3.C: New.
* g++.dg/cpp0x/constexpr-noexcept4.C: New.
* g++.dg/cpp0x/constexpr-non-const-arg.C: New.
* g++.dg/cpp0x/constexpr-nonlit.C: New.
* g++.dg/cpp0x/constexpr-nonlit2.C: New.
* g++.dg/cpp0x/constexpr-nullptr.C: New.
* g++.dg/cpp0x/constexpr-object1.C: New.
* g++.dg/cpp0x/constexpr-object2.C: New.
* g++.dg/cpp0x/constexpr-overflow.C: New.
* g++.dg/cpp0x/constexpr-pedantic.C: New.
* g++.dg/cpp0x/constexpr-pos1.C: New.
* g++.dg/cpp0x/constexpr-potential1.C: New.
* g++.dg/cpp0x/constexpr-ptrmem.C: New.
* g++.dg/cpp0x/constexpr-pure.C: New.
* g++.dg/cpp0x/constexpr-static.C: New.
* g++.dg/cpp0x/constexpr-static2.C: New.
* g++.dg/cpp0x/constexpr-static3.C: New.
* g++.dg/cpp0x/constexpr-static4.C: New.
* g++.dg/cpp0x/constexpr-static5.C: New.
* g++.dg/cpp0x/constexpr-static6.C: New.
* g++.dg/cpp0x/constexpr-string.C: New.
* g++.dg/cpp0x/constexpr-switch.C: New.
* g++.dg/cpp0x/constexpr-targ.C: New.
* g++.dg/cpp0x/constexpr-throw.C: New.
* g++.dg/cpp0x/constexpr-typeid.C: New.
* g++.dg/cpp0x/constexpr-union.C: New.
* g++.dg/cpp0x/constexpr-value.C: New.
* g++.dg/cpp0x/constexpr-value2.C: New.
* g++.dg/cpp0x/constexpr-variadic.C: New.
* g++.dg/cpp0x/initlist5.C: Use constexpr.
* g++.dg/cpp0x/static_assert3.C: Add expected error.
* g++.dg/other/fold1.C: Likewise.
* g++.dg/parse/crash36.C: Likewise.
* g++.dg/parse/constant4.C: Adjust expected error.
* g++.dg/template/arg5.C: Likewise.
* g++.dg/template/non-dependent10.C: Likewise.
* g++.dg/template/qualified-id3.C: Likewise.
* g++.dg/warn/overflow-warn-1.C: Likewise.
* g++.dg/warn/overflow-warn-3.C: Likewise.
* g++.dg/warn/overflow-warn-4.C: Likewise.
* g++.old-deja/g++.pt/crash10.C: Likewise.
* g++.dg/cpp0x/constexpr-ctor2.C: New.

View File

@ -0,0 +1,15 @@
// { dg-options -std=c++0x }
template<class T>
constexpr T do_get(T* x, int n) {
return x[n - 1];
}
template<class T, int N>
constexpr T get(T (&x)[N]) {
return do_get(x, N);
}
constexpr int arr_i[] = {1};
constexpr auto var = get(arr_i); // #2
static_assert(var == arr_i[0], "Error");

View File

@ -0,0 +1,21 @@
// { dg-options -std=c++0x }
template<class T>
struct IsNegative {
int dummy; // Workaround for empty class problem
constexpr IsNegative() : dummy(0) {}
constexpr bool operator()(const T& x) {
return x < T(0);
}
};
template<class T, int N, class Pred>
constexpr bool has_neg(T (&x)[N], Pred p) {
return p(x[0]) || p(x[1]);
}
constexpr int a[] = {1, -2};
constexpr auto answer = has_neg(a, IsNegative<int>{}); // #1
static_assert(answer, "Error");

View File

@ -0,0 +1,19 @@
// { dg-options -std=c++0x }
constexpr bool is_negative(int x) {
return x < 0;
};
constexpr bool do_has_neg(const int* x, bool(*p)(int)) {
return p(x[0]) || p(x[1]); // Line 6
}
constexpr bool has_neg(const int (&x)[2], bool(*p)(int)) {
return do_has_neg(x, p); // Line 10
}
constexpr int a[] = {1, -2};
constexpr auto answer = has_neg(a, is_negative); // Line 15
static_assert(answer, "Error");

View File

@ -0,0 +1,23 @@
// { dg-options -std=c++0x }
constexpr const int do_last(const int* x, int n) {
return x[n - 1];
}
struct IsNegative {
constexpr bool operator()(const int& x) {
return x < 0;
}
};
template<int N, class Pred>
constexpr bool has_neg(const int (&x)[N], Pred p) {
return p(do_last(x, N)); // Line 13
}
constexpr int a[] = {1, -2};
constexpr auto answer = has_neg(a, IsNegative{}); // Line 18
static_assert(answer, "Error");

View File

@ -0,0 +1,32 @@
// { dg-options -std=c++0x }
template<class T>
constexpr T do_last(T* x, int n) {
return x[n - 1]; //
}
template<class T, int N>
constexpr T last(T (&x)[N]) {
return do_last(x, N);
}
constexpr bool is_negative(int x) { return x < 0; }
template<class T>
struct IsNegative {
constexpr bool operator()(const T& x) {
return x < T(0);
}
};
template<class T, int N, class Pred>
constexpr bool has_neg(T (&x)[N], Pred p) {
return p(last(x)); // Line 22
}
constexpr int a[] = {1, -2};
constexpr auto answer1 = has_neg(a, IsNegative<int>{}); // Line 27
constexpr auto answer2 = has_neg(a, is_negative);
static_assert(answer2 == answer1, "Error");

View File

@ -0,0 +1,18 @@
// { dg-options "-std=c++0x" }
typedef decltype(sizeof(char)) size_type;
template<class T, size_type N>
constexpr size_type size(T (&)[N]) { return N; }
double array_double[] = { 1.0, 2.0, 3.0 };
constexpr auto sz_d = size(array_double);
static_assert(sz_d == 3, "Array size failure");
void f(bool (&param)[2]) {
static_assert(size(param) == 2, "Array size failure"); // Line 13
short data[] = {-1, 2, -45, 6, 88, 99, -345};
static_assert(size(data) == 7, "Array size failure");
}

View File

@ -0,0 +1,15 @@
// { dg-options -std=c++0x }
// { dg-final { scan-assembler-not "static_initialization" } }
struct A
{
int i;
constexpr A(): i(0) { }
};
struct B
{
A a[4];
};
extern const B b{};

View File

@ -0,0 +1,17 @@
// Test base/member class and static_assert with constexpr
// { dg-options -std=c++0x }
struct A {
int i;
constexpr A(int _i): i(_i) { }
};
struct B: A {
A a;
int j;
constexpr B(int _ib, int _ia, int _j): A(_ib), a(_ia), j(_j) { }
};
constexpr B b (12, 24, 36);
#define SA(X) static_assert (X, #X)
SA(b.i==12 && b.a.i==24 && b.j==36);

View File

@ -0,0 +1,17 @@
// Make sure C99 complex works with constexpr
// { dg-options -std=c++0x }
struct complex
{
typedef float value_type;
typedef __complex__ float _ComplexT;
constexpr complex(_ComplexT __z) : _M_value(__z) { }
constexpr complex(float __r = 0.0f, float __i = 0.0f)
: _M_value(__r + __i * 1.0fi) { }
private:
_ComplexT _M_value;
};
constexpr complex c1;

View File

@ -0,0 +1,43 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
// From N2235
// 1
struct A2
{
static const int eights = 888;
static constexpr int nines = 999;
};
A2 a;
// 2
struct pixel
{
int x, y;
};
constexpr pixel ur = { 1294, 1024 }; // OK
// p4
struct Length
{
explicit constexpr Length(int i = 0) : val(i) { }
private:
int val;
};
constexpr int myabs(int x)
{ return x < 0 ? -x : x; } // OK
Length l(myabs(-97)); // OK
// p6
class debug_flag
{
public:
explicit debug_flag(bool);
constexpr bool is_on(); // { dg-error "enclosing class .* not a literal type" }
private:
bool flag;
};

View File

@ -0,0 +1,48 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
template<typename _Tp, _Tp v>
struct A3
{
typedef _Tp value_type;
typedef A3<value_type,v> type;
static constexpr value_type value = v;
constexpr operator value_type() { return value; }
};
// Partial specialization.
template<typename _Tp, _Tp v>
struct A3<_Tp*, v>
{
typedef _Tp* value_type;
typedef A3<value_type,v> type;
static constexpr value_type value = v;
constexpr operator value_type() { return value; }
};
// Explicit specialization.
template<>
struct A3<unsigned short, 0>
{
typedef unsigned short value_type;
typedef A3<value_type, 0> type;
static constexpr value_type value = 0;
constexpr operator value_type() { return value; }
};
// Explicitly instantiate.
template struct A3<int, 415>;
// Extern explicitly instantiate.
extern template struct A3<int, 510>;
// Use.
A3<int, 1111> a31;
// FIXME should this be an error?
A3<char, 9999> a32; // { dg-warning "overflow" }

View File

@ -0,0 +1,16 @@
// { dg-options -std=c++0x }
struct A
{
const int *p[2];
};
constexpr const int * f(const int *p) { return p; }
int main()
{
constexpr int i = 42;
constexpr int j = *&i; // OK
constexpr int k = *A{{&i}}.p[0]; // OK
constexpr int l = *f(&i); // OK
}

View File

@ -0,0 +1,21 @@
// Test that we explain why a template instantiation isn't constexpr
// { dg-options -std=c++0x }
// { dg-prune-output "not a constexpr function" }
template <class T>
struct A
{
T t;
constexpr int f() { return 42; }
};
struct B { B(); operator int(); };
constexpr A<int> ai = { 42 };
constexpr int i = ai.f();
constexpr int b = A<B>().f(); // { dg-error "enclosing class" }
template <class T>
constexpr int f (T t) { return 42; }
constexpr int x = f(B()); // { dg-error "parameter" }

View File

@ -0,0 +1,9 @@
// { dg-options -std=c++0x }
template<class T> class my_limits {
public:
static constexpr T min() throw() { return T(); }
static constexpr T max() noexcept { return T(); }
};
constexpr double var_min = my_limits<double>::min(); // #1 OK
constexpr double var_max = my_limits<double>::max(); // #2 Error

View File

@ -0,0 +1,5 @@
// { dg-options -std=c++0x }
constexpr int ellipsis(...) { return 1; }
constexpr int ellipsis_c = ellipsis(); // OK
constexpr int ellipsis_c2 = ellipsis(42); // Internal error

View File

@ -0,0 +1,12 @@
// { dg-options -std=c++0x }
struct A
{
A();
A(const A&);
bool empty();
};
constexpr int ellipsis(...) { return 1; }
static_assert(ellipsis(A().empty()), "Error"); // { dg-error "non-constant condition|empty" }

View File

@ -0,0 +1,7 @@
// { dg-options -std=c++0x }
struct Empty {};
constexpr bool f(Empty) { return true; }
constexpr bool x(f(Empty{}));

View File

@ -0,0 +1,7 @@
// { dg-options -std=c++0x }
struct IsLiteral {};
constexpr IsLiteral bar(IsLiteral x) { return x; }
constexpr auto xy = bar(IsLiteral()); // #1 Error, but should be OK

View File

@ -0,0 +1,9 @@
// { dg-options -std=c++0x }
struct IsLiteral {};
constexpr auto ab = IsLiteral();
constexpr IsLiteral bar(IsLiteral x) { return x; }
constexpr auto xy = bar(ab);

View File

@ -0,0 +1,34 @@
// { dg-options -std=c++0x }
typedef decltype(sizeof(char)) size_type;
template<class T, size_type N, class Pred>
constexpr size_type do_find_if_or_stop(T (&x)[N], size_type i, Pred p);
template<class T, size_type N, class Pred>
constexpr size_type do_find_if(T (&x)[N], size_type i, Pred p) {
return p(x[i]) ? i : do_find_if_or_stop(x, i + 1, p); // line 8
}
template<class T, size_type N, class Pred>
constexpr size_type do_find_if_or_stop(T (&x)[N], size_type i, Pred p) {
return i == N ? N : do_find_if(x, i, p);
} // Line 14
template<class T, size_type N, class Pred>
constexpr size_type find_if(T (&x)[N], Pred p) {
return do_find_if(x, 0, p); // Line 18
}
constexpr long items_long[] = {1, 2, 3, 4, -5, 6, -7, 8};
template<class T>
struct IsNegative {
constexpr bool operator()(const T& x) {
return x < T(0);
}
};
constexpr auto pos1 = find_if(items_long, IsNegative<long>{}); // Line 30
static_assert(pos1 == 4, "find_if failure");

View File

@ -0,0 +1,7 @@
// { dg-options -std=c++0x }
struct A { };
struct B: A { };
constexpr B b { };
constexpr A a = b;

View File

@ -0,0 +1,94 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
// From N2235
// 4.1 constant-expression functions
// 1 examples
// 2 defined before first use
// NOTE: this is only needed in contexts that require a constant-expression
struct S {
constexpr int twice();
constexpr int t(); // { dg-message "used but never defined" }
private:
static constexpr int val; // constexpr variable
};
constexpr int S::val = 7;
constexpr int S::twice() { return val + val; }
constexpr S s = { };
int x1 = s.twice(); // ok
int x2 = s.t(); // error: S::t() not defined
constexpr int x2a = s.t(); // { dg-error "S::t" } error: S::t() not defined
constexpr int ff(); // ok
constexpr int gg(); // ok
int x3 = ff(); // error: ff() not defined
constexpr int x3a = ff(); // { dg-error "ff" } error: ff() not defined
constexpr int ff() { return 1; } // too late
constexpr int gg() { return 2; }
int x4 = gg(); // ok
// 4.2 const-expression data
// 2
// storage not allocated untill address taken
constexpr double x = 9484.748;
const double* p = &x; // the &x forces x into memory
// 4.3 constant-expression constructors
// 1
struct complex {
constexpr complex(double r, double i) : re(r), im(i) { }
constexpr double real() { return re; }
constexpr double imag() { return im; }
private:
double re;
double im;
};
constexpr complex I(0, 1); // OK -- literal complex
// 2 invoked with non-const args
double x5 = 1.0;
constexpr complex unit(x5, 0); // { dg-error "x5|argument" } error: x5 non-constant
const complex one(x5, 0); // OK, ordinary const -- dynamic
// initialization
constexpr double xx = I.real(); // OK
complex z(2, 4); // OK -- ordinary variable
// 3
constexpr complex v[] = {
complex(0, 0), complex(1, 1), complex(2, 2)
};
constexpr double x6 = v[2].real(); // OK
// 4
constexpr int i = 98;
typedef __INTPTR_TYPE__ intptr_t;
constexpr intptr_t ip = (intptr_t) &i; // { dg-error "constant" }
// 4.3.2 copy-constructor
constexpr complex operator+(complex z, complex w)
{
return complex(z.real() + w.real(), z.imag() + w.imag()); // fine
}
constexpr complex I2 = I + I; // OK
struct resource {
int id;
constexpr resource(int i) : id(i) { } // fine
resource(const resource& r) : id(r.id) // oops, not constexpr
{
//cout << id << " copied" << endl;
}
};
constexpr resource f(resource d)
{ return d; } // { dg-error "not .constexpr" }
constexpr resource d = f(9); // { dg-error "resource" }
// 4.4 floating-point constant expressions

View File

@ -0,0 +1,23 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
// From N2235
// 4.5.3 constant expressions
// p 4
struct A {
constexpr A(int i) : val(i) { }
constexpr operator int() { return val; }
constexpr operator long() { return -1; }
private:
int val;
};
template<int I> struct X { static const int i = I; };
constexpr A a = 42;
X<a> x; // OK: unique conversion to int
int ar[X<a>::i]; // also OK
int ary[a]; // { dg-error "ambiguous|conversion|array" } ambiguous conversion

View File

@ -0,0 +1,30 @@
// { dg-options "-std=c++0x" }
#define SA(X) static_assert (X, #X)
struct A
{
int i;
constexpr A(int _i) { i = _i; } // { dg-error "empty body|uninitialized member" }
};
template <class T>
struct B
{
T t;
constexpr B(T _t): t(_t) { }
};
B<int> b(1);
SA(b.t==1); // { dg-error "non-constant condition|'b'" }
constexpr B<int> b2(1);
SA(b2.t==1);
template <class T>
constexpr T f(T a, T b)
{
typedef T myT;
return a + b;
}
SA(f(1,2)==3);

View File

@ -0,0 +1,16 @@
// { dg-options "-std=c++0x" }
struct A
{
constexpr A(int) { }
constexpr operator int() { return 1; };
};
template <class T>
struct B
{
static constexpr A a = A(1);
int ar[a];
};
B<int> b;

View File

@ -0,0 +1,9 @@
// { dg-options -std=c++0x }
template<class T> constexpr inline T bar(T x) { return x; }
template short bar(short x); // #EI
constexpr auto yz = bar(0); // OK
constexpr auto ab = bar(short()); // #1 Error, but should be OK
constexpr auto mn = bar(short{}); // #2 Error, but should be OK

View File

@ -0,0 +1,7 @@
// { dg-options -std=c++0x }
constexpr bool is_negative(int x) { return x < 0; }
constexpr bool check(int x, bool (*p)(int)) { return p(x); } // #1
static_assert(check(-2, is_negative), "Error");

View File

@ -0,0 +1,10 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
// From N2235
constexpr int veryabs(int x) { return x < 0 ? -x : x; }
constexpr long long_max() { return 2147483647; }
constexpr int verysquare(int x) { return x * x; }

View File

@ -0,0 +1,50 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
// From N2235
// Mess with the builtin by redeclaring.
constexpr int abs(int x) { return x < 0 ? -x : x; }
extern "C"
{
constexpr float
squaref(float x) { return x * x; }
}
// implicitly inline, already: warn?
inline constexpr double
squared(double x) { return x * x; }
constexpr int squarei(int x) { return x * x; }
extern const int side;
constexpr int area = squarei(side); // { dg-error "side|argument" }
// error: squarei(side) is not a constant expression
int next(constexpr int x) // { dg-error "parameter" }
{ return x + 1; }
constexpr void f(int x) // { dg-error "return type .void" }
{ /* ... */ }
constexpr int prev(int x)
{ return --x; } // { dg-error "--" }
constexpr int g(int x, int n) // error: body not just return expr
{
int r = 1;
while (--n > 0) r *= x;
return r;
} // { dg-error "not a return-statement" }
constexpr int
bar(int x, int y) { return x + y + x * y; } // { dg-error "previously" }
int bar(int x, int y) // { dg-error "redefinition" }
{ return x * 2 + 3 * y; }
constexpr int twice(int x); // { dg-message "never defined" }
enum { bufsz = twice(256) }; // { dg-error "" } twice() isnt (yet) defined
constexpr int fac(int x)
{ return x > 2 ? x * fac(x - 1) : 1; } // OK

View File

@ -0,0 +1,30 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
// From N2235
// function template 1
template<typename T>
constexpr int bytesize(T t)
{ return sizeof (t); } // OK
char buf[bytesize(0)]; // OK -- not C99 VLA
// function template 2
template<typename _Tp>
constexpr _Tp
square(_Tp x) { return x; }
// Explicit specialization
template<>
constexpr unsigned long
square(unsigned long x) { return x * x; }
// Explicit instantiation
template int square(int);
class A { };
template A square(A);
template long square(long);

View File

@ -0,0 +1,11 @@
// We used to crash on this instead of giving a decent error.
// { dg-options -std=c++0x }
struct A { int i; };
struct B {
const A *a;
constexpr B(const A& a): a(&a) { }
};
constexpr B b{A{42}}; // { dg-error "constant|expansion" }

View File

@ -0,0 +1,65 @@
// { dg-options -std=c++0x }
// { dg-do run }
namespace xstd {
typedef decltype(sizeof(char)) size_t;
template<class E>
class initializer_list {
private:
size_t sz;
const E* start;
public:
typedef E value_type;
typedef const E& reference;
typedef const E& const_reference;
typedef size_t size_type;
typedef const E* iterator;
typedef const E* const_iterator;
constexpr initializer_list() : sz(), start(nullptr) {}
template<size_t N>
constexpr initializer_list(const E(&array)[N]) : sz(N), start(array) {}
constexpr size_t size() { return sz; }
constexpr const E* begin() { return start; }
constexpr const E* end() { return start + sz; }
};
template<class E, size_t N>
constexpr initializer_list<E> make_list(const E(&array)[N]) {
return initializer_list<E>(array);
}
template<class E>
E min(initializer_list<E> list)
{
// static_assert(list.size() > 0, "Invalid list");
auto it = list.begin();
E result = *it;
for (++it; it != list.end(); ++it) {
if (*it < result) {
result = *it;
}
}
return result;
}
}
constexpr int global_i[] = {2, 4, -5, 6, 10};
constexpr xstd::initializer_list<int> list(global_i);
#define SA(X) static_assert(X, #X)
SA(list.size() == 5);
SA(list.begin()[2] == -5);
SA(list.end()[-1] == 10);
int main() {
if (xstd::min(xstd::make_list(global_i)) != -5)
return 1;
}

View File

@ -0,0 +1,69 @@
// Negative examples from N3092 (FCD)
// { dg-options -std=c++0x }
// OK: declaration
constexpr int square(int x); // { dg-message "never defined" }
// error: pixel is a type
constexpr struct pixel {
int x;
int y;
// OK: declaration
constexpr pixel(int);
}; // { dg-error "constexpr" }
constexpr pixel::pixel(int a)
// OK: definition
: x(square(a)), y(square(a)) // { dg-error "square" }
{ }
// error: square not defined, so small(2) not constant (5.19), so constexpr
// not satisfied
constexpr pixel small(2); // { dg-error "" }
// error: not for parameters
int next(constexpr int x) { // { dg-error "parameter" }
return x + 1;
}
// error: not a definition
extern constexpr int memsz; // { dg-error "definition" }
// error: return type is void
constexpr void f(int x) // { dg-error "void" }
{ /* ... */ }
// error: use of decrement
constexpr int prev(int x)
{ return --x; } // { dg-error "-- x" }
// error: body not just return expr
constexpr int g(int x, int n) {
int r = 1;
while (--n > 0) r *= x;
return r;
} // { dg-error "body of constexpr function" }
class debug_flag {
public:
explicit debug_flag(bool);
constexpr bool is_on(); // { dg-error "not a literal type" } debug_flag not literal type
private:
bool flag;
};
// OK
constexpr int bar(int x, int y) // { dg-error "previously defined here" }
{ return x + y + x*y; }
// ...
// error: redefinition of bar
int bar(int x, int y) // { dg-error "redefinition" }
{ return x * 2 + 3 * y; }
struct pixel2 { // { dg-message "no user-provided default constructor" }
int x, y;
};
constexpr pixel2 ur = { 1294, 1024 };// OK
constexpr pixel2 origin; // { dg-error "uninitialized const" }
constexpr const int* addr(const int& ir) { return &ir; } // OK
// error, initializer for constexpr variable not a constant
extern constexpr const int* tp = addr(5); // { dg-error "" }

View File

@ -0,0 +1,13 @@
// { dg-options -std=c++0x }
template<class T>
struct is_funny {
static constexpr bool value = false;
};
template<class T>
constexpr T value(T t) noexcept(is_funny<T>::value) { return t; } // Line 7
constexpr bool ok = noexcept(value(42));
static_assert(ok, "Assertion failure");

View File

@ -0,0 +1,14 @@
// { dg-options -std=c++0x }
template<class T>
constexpr T value(T t) { return t; }
template<class T>
struct is_funny {
static constexpr bool value = false;
};
template<class T>
void eval() noexcept(value(is_funny<T>::value)) {}
constexpr bool ok = noexcept(eval<int>()); // line 12

View File

@ -0,0 +1,7 @@
// { dg-options -std=c++0x }
constexpr int f(int i) { return i; }
#define SA(X) static_assert (X, #X)
SA(noexcept(f(42)));
int j;
SA(!noexcept(f(j)));

View File

@ -0,0 +1,14 @@
// { dg-options -std=c++0x }
// A call is noexcept if it is a valid subexpression of a constant
// expression, even if it is not itself a constant expression.
#define SA(X) static_assert(X,#X)
constexpr const int* f(const int *p) { return p; }
int main()
{
constexpr int i = 42;
SA(noexcept(*f(&i)));
SA(noexcept(f(&i)));
}

View File

@ -0,0 +1,24 @@
// Example from issue 1125 drafting; D() and v were well-formed with the
// wording approved in Rapperswil, now seems they should be ill-formed.
// { dg-options "-std=c++0x -pedantic-errors" }
struct B {
constexpr B(int x) : i(0) { } // "x" is unused
int i;
};
int global; // not constant
struct D : B {
constexpr D() : B(global) { } // { dg-error "global|argument" }
};
struct A2 {
constexpr A2(bool b, int x) : m(b ? 42 : x) { }
int m;
};
// ok, constructor call initializes m with the value 42 after substitution
constexpr int v = A2(true, global).m; // { dg-error "global" }
// error: initializer for m is "x", which is non-constant
constexpr int w = A2(false, global).m; // { dg-error "global" }

View File

@ -0,0 +1,13 @@
// FIXME this is currently invalid, but seems like it should be OK
// { dg-options -std=c++0x }
struct A { A() { } };
template<class T>
constexpr bool ignore(T&&) { return true; }
static_assert(ignore(10), "Error"); // OK
A s;
static_assert(ignore(s), "Error"); // Currently an error

View File

@ -0,0 +1,19 @@
// { dg-options -std=c++0x }
struct A
{
~A();
};
template<class T>
struct W {
T t;
template<class U>
constexpr W(U&& u) : t(u) {}
};
template <class T>
constexpr W<T> make_w(T& w) { return W<T>(w); }
A a;
constexpr auto w = make_w(a); // { dg-error "" }

View File

@ -0,0 +1,6 @@
// { dg-options -std=c++0x }
constexpr int zero() { return 0; }
void* ptr1 = zero(); // #1
constexpr void* ptr2 = zero(); // #2

View File

@ -0,0 +1,31 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
// From N2235
// 4.5.2 semantics
// p 1 constexpr specifier
// objects, static const data
struct A1 { }; // { dg-message "no user-provided default constructor" }
constexpr int i1 = 1024;
constexpr A1 a1 = A1();
// error: not a definition
extern constexpr int i2; // { dg-error "definition" }
// error: missing initializer
constexpr A1 a2; // { dg-error "uninitialized const" }
// error: duplicate cv
const constexpr A1 a3 = A1(); // { dg-error "both .const. and .constexpr. cannot" }
volatile constexpr A1 a4 = A1(); // { dg-error "both .volatile. and .constexpr. cannot" }
// error: on type declaration
constexpr struct pixel
{
int x;
int y;
}; // { dg-error "cannot be used for type declarations" }

View File

@ -0,0 +1,16 @@
// { dg-do "compile" }
// { dg-options "-std=gnu++0x" }
constexpr int verysquare(int x) { return x * x; }
const double mass = 9.8;
constexpr double energy = mass * verysquare(56.6); // { dg-error "mass" "" { xfail *-*-* } }
int arr[(int)mass]; // { dg-error "mass" "" { xfail *-*-* } }
float array[verysquare(9)]; // OK -- not C99 VLA
extern const int medium;
const int high = verysquare(medium); // OK -- dynamic initialization
enum { Max = verysquare(7) }; // OK

View File

@ -0,0 +1,6 @@
// { dg-options "-std=c++0x -w" }
#include <limits.h>
extern constexpr int max_s = INT_MAX + 1; // { dg-error "" }
extern constexpr unsigned max_u = UINT_MAX + 1u; // OK
extern constexpr int abs_s = -INT_MIN; // { dg-error "" } overflows on 2's complement machines

View File

@ -0,0 +1,16 @@
// The FCD doesn't allow typedefs and static_assert in constexpr functions,
// but it should.
// { dg-options "-std=c++0x -pedantic" }
template <class T>
constexpr T f(T t)
{
typedef T T2; // { dg-warning "constexpr" "" { xfail *-*-* } }
static_assert (T2(0) == T(0), ""); // { dg-warning "constexpr" "" { xfail *-*-* } }
return t;
}
int main()
{
constexpr int i = f(42);
}

View File

@ -0,0 +1,60 @@
// Positive examples from N3092 (FCD)
// { dg-options -std=c++0x }
#define SA(X) static_assert(X, #X)
constexpr int bufsz = 1024; // OK: definition
SA (bufsz == 1024);
constexpr int square(int x); // OK: declaration
struct pixel {
int x;
int y;
// OK: declaration
constexpr pixel(int);
};
constexpr pixel::pixel(int a) // OK: definition
: x(square(a)), y(square(a))
{ }
constexpr int square(int x) // OK: definition
{ return x * x; }
constexpr pixel large(4); // OK: square defined
SA(large.x == 16 && large.y==16);
constexpr long long_max() // OK
{ return 2147483647; }
SA(long_max() == 2147483647);
constexpr int abs(int x) // OK
{ return x < 0 ? -x : x; }
SA(abs(-1) == 1);
SA(abs(24) == 24);
struct Length {
explicit constexpr Length(int i = 0) : val(i) { }
private:
int val;
};
constexpr Length l1;
constexpr Length l2(12);
struct pixel2 {
int x, y;
};
constexpr pixel2 ur = { 1294, 1024 };// OK
SA(ur.x == 1294 && ur.y == 1024);
constexpr const int* addr(const int& ir) { return &ir; } // OK
static const int x = 5;
extern constexpr const int* xp = addr(x); // OK: (const int*)&(const int&)x
// is an address contant expression
SA(xp == &x);
extern constexpr int x2 = *addr(5);
SA(x2 == 5);

View File

@ -0,0 +1,7 @@
// { dg-options -std=c++0x }
// We decided in Rapperswil that it's OK if any value of decide can produce
// a constant expression.
constexpr int may_throw(bool decide) {
return decide ? 42 : throw -1;
}

View File

@ -0,0 +1,20 @@
// { dg-options -std=c++0x }
struct C { // literal type
int m;
int n;
constexpr C(int m) : m(m), n(-m) {}
constexpr bool is_neg() { return m < 0; }
};
constexpr bool check1(const C& c, int C:: *pm) { return c.*pm < 0; } // #1
constexpr bool check2(const C* pc, bool (C::*pm)() const) { return
(pc->*pm)(); } // #2
constexpr C c(-1);
static_assert(!check1(c, &C::n), "Error");
static_assert(check1(c, &C::m), "Error");
static_assert(check2(&c, &C::is_neg), "Error");

View File

@ -0,0 +1,13 @@
// { dg-options -std=c++0x }
struct A
{
virtual void f() = 0;
};
struct B: A
{
void f() { }
};
B b;

View File

@ -0,0 +1,24 @@
// Test for constant initialization of non-literal class (e.g. mutex)
// { dg-options "-std=c++0x -save-temps" }
// { dg-do run }
struct A
{
int i;
constexpr A(int _i): i(_i) { }
A(const A&); // non-trivial copy ctor makes A non-literal
};
A a(42); // constexpr constructor allows constant initialization
A ar[3] = { { 1 }, { 2 }, { 3 } };
// { dg-final { scan-assembler-not "static_initialization" } }
// { dg-final cleanup-saved-temps }
int main()
{
if (a.i != 42
|| ar[0].i != 1
|| ar[1].i != 2
|| ar[2].i != 3)
return 1;
}

View File

@ -0,0 +1,11 @@
// { dg-options -std=c++0x }
struct IsLiteral {};
struct ShouldBeLiteral {
constexpr ShouldBeLiteral(int){}
};
struct StaticDataMember {
static constexpr IsLiteral one = IsLiteral(); // #1
static constexpr ShouldBeLiteral two= ShouldBeLiteral(-1); // #2
};

View File

@ -0,0 +1,18 @@
// Test for constant initialization of class with vtable
// { dg-options "-std=c++0x -save-temps" }
// { dg-final { scan-assembler-not "static_initialization" } }
// { dg-final cleanup-saved-temps }
// { dg-do run }
int r = 1;
// implicit default constructor for A and B is constexpr
struct A { virtual void f() {} };
struct B: A { virtual void f() { r = 0; } };
B b;
int main()
{
b.f();
return r;
}

View File

@ -0,0 +1,20 @@
// { dg-options -std=c++0x }
// { dg-do run }
extern "C" void abort ();
extern int ar[2];
int f()
{
if (ar[0] != 42 || ar[1] != 0)
abort ();
return 1;
}
int i = f();
int ar[2] = { 42, i };
int main()
{
}

View File

@ -0,0 +1,17 @@
// { dg-options -std=c++0x }
template <class T>
struct A
{
constexpr static T t;
};
template <class T>
constexpr T A<T>::t = T(); // { dg-error "not literal" }
struct B
{
~B();
};
B b = A<B>::t;

View File

@ -0,0 +1,21 @@
// { dg-options -std=c++0x }
struct B
{
constexpr operator int() { return 4; }
};
template <int I>
struct C;
template<>
struct C<4> { typedef int TP; };
template <class T>
struct A
{
constexpr static B t = B();
C<t>::TP tp;
};
A<B> a;

View File

@ -0,0 +1,5 @@
// { dg-options -std=c++0x }
constexpr char c1 = "hi"[1];
constexpr char c2 = "hi"[2];
constexpr char c3 = "hi"[3]; // { dg-error "out of bound" }

View File

@ -0,0 +1,13 @@
// { dg-options -std=c++0x }
template<class T>
constexpr T value(T t = T()) { return t; }
enum us_enum { us_item = value<short>() }; // OK
void func(us_enum n) {
switch (n) {
case value(us_item): ; // #1 Error
default: ;
}
}

View File

@ -0,0 +1,13 @@
// { dg-options -std=c++0x }
struct A
{
constexpr operator double() { return 1.0; }
};
template <int I>
struct B
{ };
constexpr A a { };
B<a> b; // { dg-error "template argument|invalid type" }

View File

@ -0,0 +1,8 @@
// { dg-options -std=c++0x }
constexpr int may_throw(bool decide) {
return decide ? 42 : throw -1; // { dg-error "throw" }
}
constexpr int x = may_throw(false); // { dg-error "may_throw" }
constexpr int y = may_throw(true);

View File

@ -0,0 +1,10 @@
// { dg-options -std=c++0x }
#include <typeinfo>
struct A { virtual void f(); };
extern constexpr const std::type_info* p1 = &typeid(int);
extern constexpr const std::type_info* p2 = &typeid(A);
// typeid-expression whose operand is of a polymorphic class type
extern constexpr const std::type_info* p3 = &typeid((A())); // { dg-error "" "" { xfail *-*-* } }

View File

@ -0,0 +1,16 @@
// Test that we don't have to deal with type punning
// FIXME Mike Miller thinks it should work
// { dg-options -std=c++0x }
union U
{
float f;
unsigned char ca[sizeof(float)];
};
constexpr U u = { 1.0 };
constexpr float f = u.f;
constexpr unsigned char c = u.ca[0]; // { dg-error "U::ca" }
constexpr double d = 1.0;
constexpr unsigned char c2 = (unsigned char&)d; // { dg-error "char. glvalue" }

View File

@ -0,0 +1,10 @@
// { dg-options -std=c++0x }
struct HopefullyLiteral {
HopefullyLiteral() = default; // Should be a constexpr c'tor as of 12.1/6 and 8.4.2/4
};
constexpr HopefullyLiteral var1{}; // OK
constexpr HopefullyLiteral var2 = HopefullyLiteral{}; // #1
constexpr HopefullyLiteral var3 = HopefullyLiteral(); // #2
constexpr HopefullyLiteral var4 = HopefullyLiteral(var3); // #3

View File

@ -0,0 +1,19 @@
// { dg-options -std=c++0x }
template<class T>
constexpr T value_init() { return T(); }
template<class T>
constexpr inline T bar(T x) { return x; }
union EmptyUnion {};
union Union1 { int i; };
union Union3 { double d; int i; char* c; };
constexpr auto u1 = value_init<EmptyUnion>();
constexpr auto u2 = value_init<Union1>();
constexpr auto u3 = value_init<Union3>();
constexpr auto u4 = bar(EmptyUnion{});
constexpr auto u5 = bar(Union1{});
constexpr auto u6 = bar(Union3{});
constexpr auto u7 = bar(u1);

View File

@ -0,0 +1,9 @@
// { dg-options -std=c++0x }
template<class... T>
constexpr bool variadics(T&&...) { return true; }
struct IsLiteral {};
constexpr bool variadic_var = variadics(0, true, 1.2, IsLiteral{}); // Error, so below
int main() {}

View File

@ -23,5 +23,5 @@ int k {}; // initialize to 0
// PR c++/36963
double d = 1.1;
float fa[] = { d, 1.1 }; // { dg-error "narrowing conversion of 'd'" }
const double d2 = 1.1;
constexpr double d2 = 1.1;
float fa2[] = { d2, 1.1 };

View File

@ -1,3 +1,4 @@
// { dg-options "-std=c++0x" }
static_assert(7 / 0, "X"); // { dg-error "non-constant condition" }
// { dg-warning "division by zero" "" { target *-*-* } 2 }
// { dg-error "7 / 0.. is not a constant expression" "" { target *-*-* } 2 }

View File

@ -4,5 +4,5 @@
struct A
{
static const int i = i; // { dg-error "not declared" }
int x[i];
int x[i]; // { dg-error "constant-expression" }
};

View File

@ -18,7 +18,7 @@ void Foo ()
static const unsigned J = X<T>::J;
Y<J> j; // { dg-error "non-constant" "" }
Y<J> j; // { dg-error "constant" "" }
}
struct A

View File

@ -9,4 +9,4 @@ template <typename... T> struct A // { dg-warning "variadic templates" }
static const int i = sizeof (++t); // { dg-error "was not declared in this scope" }
};
int x[A <int>::i];
int x[A <int>::i]; // { dg-error "constant-expression" }

View File

@ -5,5 +5,5 @@ template<bool> struct A;
template<int> void foo()
{
A<__builtin_constant_p(.)> a; // { dg-error "template argument" }
A<__builtin_constant_p(.)> a; // { dg-error "template argument|invalid" }
}

View File

@ -18,5 +18,5 @@ struct X
template <class T>
struct Foo
{
X<&S::f> x; // { dg-error "convert|no type" }
X<&S::f> x; // { dg-error "convert|no matches" }
};

View File

@ -3,7 +3,7 @@
template <const int N> struct A { };
template <class T> struct B {
static const int c;
typedef A<B<T>::c> C; // { dg-error "non-constant" }
typedef A<B<T>::c> C; // { dg-error "constant expression" }
};
template <class T> const int B<T>::c = sizeof (T);

View File

@ -13,7 +13,7 @@ enum e {
in the standard). */
E2 = 2 || 1 / 0, /* { dg-bogus "warning: division by zero" "" { xfail *-*-* } 14 } */
E3 = 1 / 0, /* { dg-warning "division by zero" } */
/* { dg-error "enumerator value for 'E3' is not an integer constant" "enum error" { target *-*-* } 15 } */
/* { dg-error "enumerator value for 'E3' is not an integer constant|not a constant expression" "enum error" { target *-*-* } 15 } */
/* But as in DR#031, the 1/0 in an evaluated subexpression means the
whole expression violates the constraints. */
E4 = 0 * (1 / 0), /* { dg-warning "division by zero" } */

View File

@ -13,7 +13,7 @@ enum e {
in the standard). */
E2 = 2 || 1 / 0, /* { dg-bogus "warning: division by zero" "" { xfail *-*-* } 14 } */
E3 = 1 / 0, /* { dg-warning "division by zero" } */
/* { dg-error "enumerator value for 'E3' is not an integer constant" "enum error" { target *-*-* } 15 } */
/* { dg-error "enumerator value for 'E3' is not an integer constant|not a constant expression" "enum error" { target *-*-* } 15 } */
/* But as in DR#031, the 1/0 in an evaluated subexpression means the
whole expression violates the constraints. */
E4 = 0 * (1 / 0), /* { dg-warning "division by zero" } */

View File

@ -13,7 +13,7 @@ enum e {
in the standard). */
E2 = 2 || 1 / 0, /* { dg-bogus "warning: division by zero" "" { xfail *-*-* } 14 } */
E3 = 1 / 0, /* { dg-warning "division by zero" } */
/* { dg-error "enumerator value for 'E3' is not an integer constant" "enum error" { target *-*-* } 15 } */
/* { dg-error "enumerator value for 'E3' is not an integer constant|not a constant expression" "enum error" { target *-*-* } 15 } */
/* But as in DR#031, the 1/0 in an evaluated subexpression means the
whole expression violates the constraints. */
E4 = 0 * (1 / 0), /* { dg-warning "division by zero" } */

View File

@ -4,7 +4,7 @@ template<int M, int N>
class GCD {
public:
enum { val = (N == 0) ? M : GCD<N, M % N>::val }; // { dg-warning "division" "division" }
// { dg-error "not a valid" "valid" { target *-*-* } 6 }
// { dg-error "constant expression" "valid" { target *-*-* } 6 }
};
int main() {

View File

@ -51,3 +51,6 @@ test04()
// { dg-error "instantiated from here" "" { target *-*-* } 46 }
// { dg-error "denominator cannot be zero" "" { target *-*-* } 153 }
// { dg-error "out of range" "" { target *-*-* } 154 }
// { dg-error "constant expression" "" { target *-*-* } 59 }
// { dg-error "not a member" "" { target *-*-* } 162 }
// { dg-error "not a valid template argument" "" { target *-*-* } 164 }