N3639 C++1y VLA support

gcc/
	* gimplify.c (gimplify_vla_decl): Don't touch an existing
	DECL_VALUE_EXPR.
gcc/cp/
	* decl.c (compute_array_index_type): Allow VLAs in C++1y mode.
	(check_array_initializer): Allow VLA init.
	(reshape_init_array_1): Adjust.
	(cp_finish_decl): Check for invalid VLA length.
	* typeck2.c (process_init_constructor_array): Adjust.
	(store_init_value): Use build_vec_init for VLAs.
	* semantics.c (add_capture): Capture VLA as ptr+len.
	(vla_capture_type): New.
	(build_capture_proxy): Rebuild the VLA.
	* typeck.c (build_simple_component_ref): Split out from...
	(build_ptrmemfunc_access_expr): ...here.
	* tree.c (array_of_runtime_bound_p): New.
	* init.c (throw_bad_array_length): New.
	(build_vec_init): Use it.
	* parser.c (cp_convert_range_for): When iterating over a VLA,
	use it directly rather than bind a reference.
	* cp-tree.h: Declare new functions.
libstdc++-v3/
	* libsupc++/new: Add std::bad_array_length.
	* libsupc++/bad_array_length.cc: New.
	* libsupc++/eh_aux_runtime.cc: Add __cxa_bad_array_length.
	* libsupc++/Makefile.in: Build them.
	* config/abi/pre/gnu.ver: Add new symbols.
	* config/abi/pre/gnu-versioned-namespace.ver: Add new symbols.

From-SVN: r198745
This commit is contained in:
Jason Merrill 2013-05-09 12:43:36 -04:00 committed by Jason Merrill
parent a3409c0279
commit 0138d6b24f
23 changed files with 331 additions and 30 deletions

View File

@ -1,5 +1,9 @@
2013-05-09 Jason Merrill <jason@redhat.com>
N3639 C++1y VLA support
* gimplify.c (gimplify_vla_decl): Don't touch an existing
DECL_VALUE_EXPR.
* tree.c (build_constructor_va): New.
* tree.h: Declare it.

View File

@ -1,3 +1,24 @@
2013-05-09 Jason Merrill <jason@redhat.com>
N3639 C++1y VLA support
* decl.c (compute_array_index_type): Allow VLAs in C++1y mode.
(check_array_initializer): Allow VLA init.
(reshape_init_array_1): Adjust.
(cp_finish_decl): Check for invalid VLA length.
* typeck2.c (process_init_constructor_array): Adjust.
(store_init_value): Use build_vec_init for VLAs.
* semantics.c (add_capture): Capture VLA as ptr+len.
(vla_capture_type): New.
(build_capture_proxy): Rebuild the VLA.
* typeck.c (build_simple_component_ref): Split out from...
(build_ptrmemfunc_access_expr): ...here.
* tree.c (array_of_runtime_bound_p): New.
* init.c (throw_bad_array_length): New.
(build_vec_init): Use it.
* parser.c (cp_convert_range_for): When iterating over a VLA,
use it directly rather than bind a reference.
* cp-tree.h: Declare new functions.
2013-05-08 Jason Merrill <jason@redhat.com>
* except.c (is_admissible_throw_operand_or_catch_parameter): Check

View File

@ -5358,6 +5358,7 @@ extern tree build_value_init_noctor (tree, tsubst_flags_t);
extern tree build_offset_ref (tree, tree, bool,
tsubst_flags_t);
extern tree throw_bad_array_new_length (void);
extern tree throw_bad_array_length (void);
extern tree build_new (vec<tree, va_gc> **, tree, tree,
vec<tree, va_gc> **, int,
tsubst_flags_t);
@ -5849,6 +5850,7 @@ extern tree get_target_expr (tree);
extern tree get_target_expr_sfinae (tree, tsubst_flags_t);
extern tree build_cplus_array_type (tree, tree);
extern tree build_array_of_n_type (tree, int);
extern bool array_of_runtime_bound_p (tree);
extern tree build_array_copy (tree);
extern tree build_vec_init_expr (tree, tree, tsubst_flags_t);
extern void diagnose_non_constexpr_vec_init (tree);
@ -6032,6 +6034,7 @@ extern tree cp_build_binary_op (location_t,
enum tree_code, tree, tree,
tsubst_flags_t);
#define cxx_sizeof(T) cxx_sizeof_or_alignof_type (T, SIZEOF_EXPR, true)
extern tree build_simple_component_ref (tree, tree);
extern tree build_ptrmemfunc_access_expr (tree, tree);
extern tree build_address (tree);
extern tree build_typed_address (tree, tree);

View File

@ -5064,7 +5064,7 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d,
tsubst_flags_t complain)
{
tree new_init;
bool sized_array_p = (max_index != NULL_TREE);
bool sized_array_p = (max_index && TREE_CONSTANT (max_index));
unsigned HOST_WIDE_INT max_index_cst = 0;
unsigned HOST_WIDE_INT index;
@ -5514,15 +5514,12 @@ check_array_initializer (tree decl, tree type, tree init)
error ("elements of array %q#T have incomplete type", type);
return true;
}
/* It is not valid to initialize a VLA. */
if (init
/* A compound literal can't have variable size. */
if (init && !decl
&& ((COMPLETE_TYPE_P (type) && !TREE_CONSTANT (TYPE_SIZE (type)))
|| !TREE_CONSTANT (TYPE_SIZE (element_type))))
{
if (decl)
error ("variable-sized object %qD may not be initialized", decl);
else
error ("variable-sized compound literal");
error ("variable-sized compound literal");
return true;
}
return false;
@ -6405,6 +6402,21 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
&& TYPE_FOR_JAVA (type) && MAYBE_CLASS_TYPE_P (type))
error ("non-static data member %qD has Java class type", decl);
if (array_of_runtime_bound_p (type))
{
/* If the VLA bound is larger than half the address space, or less
than zero, throw std::bad_array_length. */
tree max = convert (ssizetype, TYPE_MAX_VALUE (TYPE_DOMAIN (type)));
/* C++1y says we should throw for length <= 0, but we have
historically supported zero-length arrays. Let's treat that as an
extension to be disabled by -std=c++NN. */
int lower = flag_iso ? 0 : -1;
tree comp = build2 (LT_EXPR, boolean_type_node, max, ssize_int (lower));
comp = build3 (COND_EXPR, void_type_node, comp,
throw_bad_array_length (), void_zero_node);
finish_expr_stmt (comp);
}
/* Add this declaration to the statement-tree. This needs to happen
after the call to check_initializer so that the DECL_EXPR for a
reference temp is added before the DECL_EXPR for the reference itself. */
@ -8289,7 +8301,7 @@ compute_array_index_type (tree name, tree size, tsubst_flags_t complain)
error ("size of array is not an integral constant-expression");
size = integer_one_node;
}
else if (pedantic && warn_vla != 0)
else if (cxx_dialect < cxx1y && pedantic && warn_vla != 0)
{
if (name)
pedwarn (input_location, OPT_Wvla, "ISO C++ forbids variable length array %qD", name);

View File

@ -2185,6 +2185,20 @@ throw_bad_array_new_length (void)
return build_cxx_call (fn, 0, NULL, tf_warning_or_error);
}
/* Call __cxa_bad_array_length to indicate that there were too many
initializers. */
tree
throw_bad_array_length (void)
{
tree fn = get_identifier ("__cxa_throw_bad_array_length");
if (!get_global_value_if_present (fn, &fn))
fn = push_throw_library_fn (fn, build_function_type_list (void_type_node,
NULL_TREE));
return build_cxx_call (fn, 0, NULL, tf_warning_or_error);
}
/* Generate code for a new-expression, including calling the "operator
new" function, initializing the object, and, if an exception occurs
during construction, cleaning up. The arguments are as for
@ -3350,6 +3364,10 @@ build_vec_init (tree base, tree maxindex, tree init,
store_constructor will handle the semantics for us. */
stmt_expr = build2 (INIT_EXPR, atype, base, init);
if (length_check)
stmt_expr = build3 (COND_EXPR, atype, length_check,
throw_bad_array_length (),
stmt_expr);
return stmt_expr;
}
@ -3467,6 +3485,9 @@ build_vec_init (tree base, tree maxindex, tree init,
if (length_check)
{
tree throw_call;
if (array_of_runtime_bound_p (atype))
throw_call = throw_bad_array_length ();
else
throw_call = throw_bad_array_new_length ();
length_check = build3 (COND_EXPR, void_type_node, length_check,
throw_call, void_zero_node);

View File

@ -9842,13 +9842,21 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr)
begin_expr = end_expr = iter_type = error_mark_node;
else
{
tree range_temp = build_range_temp (range_expr);
pushdecl (range_temp);
cp_finish_decl (range_temp, range_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
tree range_temp;
range_temp = convert_from_reference (range_temp);
if (TREE_CODE (range_expr) == VAR_DECL
&& array_of_runtime_bound_p (TREE_TYPE (range_expr)))
/* Can't bind a reference to an array of runtime bound. */
range_temp = range_expr;
else
{
range_temp = build_range_temp (range_expr);
pushdecl (range_temp);
cp_finish_decl (range_temp, range_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
range_temp = convert_from_reference (range_temp);
}
iter_type = cp_parser_perform_range_for_lookup (range_temp,
&begin_expr, &end_expr);
}

View File

@ -9379,6 +9379,21 @@ build_capture_proxy (tree member)
name = DECL_NAME (member);
type = lambda_proxy_type (object);
if (TREE_CODE (type) == RECORD_TYPE
&& TYPE_NAME (type) == NULL_TREE)
{
/* Rebuild the VLA type from the pointer and maxindex. */
tree field = next_initializable_field (TYPE_FIELDS (type));
tree ptr = build_simple_component_ref (object, field);
field = next_initializable_field (DECL_CHAIN (field));
tree max = build_simple_component_ref (object, field);
type = build_array_type (TREE_TYPE (TREE_TYPE (ptr)),
build_index_type (max));
object = convert (build_reference_type (type), ptr);
object = convert_from_reference (object);
}
var = build_decl (input_location, VAR_DECL, name, type);
SET_DECL_VALUE_EXPR (var, object);
DECL_HAS_VALUE_EXPR_P (var) = 1;
@ -9400,6 +9415,28 @@ build_capture_proxy (tree member)
return var;
}
/* Return a struct containing a pointer and a length for lambda capture of
an array of runtime length. */
static tree
vla_capture_type (tree array_type)
{
static tree ptr_id, max_id;
if (!ptr_id)
{
ptr_id = get_identifier ("ptr");
max_id = get_identifier ("max");
}
tree ptrtype = build_pointer_type (TREE_TYPE (array_type));
tree field1 = build_decl (input_location, FIELD_DECL, ptr_id, ptrtype);
tree field2 = build_decl (input_location, FIELD_DECL, max_id, sizetype);
DECL_CHAIN (field2) = field1;
tree type = make_node (RECORD_TYPE);
finish_builtin_struct (type, "__cap", field2, NULL_TREE);
TYPE_NAME (type) = NULL_TREE;
return type;
}
/* From an ID and INITIALIZER, create a capture (by reference if
BY_REFERENCE_P is true), add it to the capture-list for LAMBDA,
and return it. */
@ -9415,7 +9452,22 @@ add_capture (tree lambda, tree id, tree initializer, bool by_reference_p,
initializer = build_x_compound_expr_from_list (initializer, ELK_INIT,
tf_warning_or_error);
type = lambda_capture_field_type (initializer, explicit_init_p);
if (by_reference_p)
if (array_of_runtime_bound_p (type))
{
/* For a VLA, we capture the address of the first element and the
maximum index, and then reconstruct the VLA for the proxy. */
gcc_assert (by_reference_p);
tree elt = cp_build_array_ref (input_location, initializer,
integer_zero_node, tf_warning_or_error);
tree ctype = vla_capture_type (type);
tree ptr_field = next_initializable_field (TYPE_FIELDS (ctype));
tree nelts_field = next_initializable_field (DECL_CHAIN (ptr_field));
initializer = build_constructor_va (ctype, 2,
ptr_field, build_address (elt),
nelts_field, array_type_nelts (type));
type = ctype;
}
else if (by_reference_p)
{
type = build_reference_type (type);
if (!real_lvalue_p (initializer))

View File

@ -871,6 +871,21 @@ build_array_of_n_type (tree elt, int n)
return build_cplus_array_type (elt, build_index_type (size_int (n - 1)));
}
/* True iff T is a C++1y array of runtime bound (VLA). */
bool
array_of_runtime_bound_p (tree t)
{
if (!t || TREE_CODE (t) != ARRAY_TYPE)
return false;
tree dom = TYPE_DOMAIN (t);
if (!dom)
return false;
tree max = TYPE_MAX_VALUE (dom);
return (!value_dependent_expression_p (max)
&& !TREE_CONSTANT (max));
}
/* Return a reference type node referring to TO_TYPE. If RVAL is
true, return an rvalue reference type, otherwise return an lvalue
reference type. If a type node exists, reuse it, otherwise create

View File

@ -2788,6 +2788,19 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
return expr;
}
/* Build a COMPONENT_REF of OBJECT and MEMBER with the appropriate
type. */
tree
build_simple_component_ref (tree object, tree member)
{
tree type = cp_build_qualified_type (TREE_TYPE (member),
cp_type_quals (TREE_TYPE (object)));
return fold_build3_loc (input_location,
COMPONENT_REF, type,
object, member, NULL_TREE);
}
/* Return an expression for the MEMBER_NAME field in the internal
representation of PTRMEM, a pointer-to-member function. (Each
pointer-to-member function type gets its own RECORD_TYPE so it is
@ -2800,7 +2813,6 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name)
{
tree ptrmem_type;
tree member;
tree member_type;
/* This code is a stripped down version of
build_class_member_access_expr. It does not work to use that
@ -2810,11 +2822,7 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name)
gcc_assert (TYPE_PTRMEMFUNC_P (ptrmem_type));
member = lookup_member (ptrmem_type, member_name, /*protect=*/0,
/*want_type=*/false, tf_warning_or_error);
member_type = cp_build_qualified_type (TREE_TYPE (member),
cp_type_quals (ptrmem_type));
return fold_build3_loc (input_location,
COMPONENT_REF, member_type,
ptrmem, member, NULL_TREE);
return build_simple_component_ref (ptrmem, member);
}
/* Given an expression PTR for a pointer, return an expression

View File

@ -795,10 +795,12 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
will perform the dynamic initialization. */
if (value != error_mark_node
&& (TREE_SIDE_EFFECTS (value)
|| array_of_runtime_bound_p (type)
|| ! reduced_constant_expression_p (value)))
{
if (TREE_CODE (type) == ARRAY_TYPE
&& TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (type)))
&& (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (type))
|| array_of_runtime_bound_p (type)))
/* For an array, we only need/want a single cleanup region rather
than one per element. */
return build_vec_init (decl, NULL_TREE, value, false, 1,
@ -1114,7 +1116,7 @@ process_init_constructor_array (tree type, tree init,
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree domain = TYPE_DOMAIN (type);
if (domain)
if (domain && TREE_CONSTANT (TYPE_MAX_VALUE (domain)))
len = (tree_to_double_int (TYPE_MAX_VALUE (domain))
- tree_to_double_int (TYPE_MIN_VALUE (domain))
+ double_int_one)

View File

@ -1382,6 +1382,10 @@ gimplify_vla_decl (tree decl, gimple_seq *seq_p)
gimplify_one_sizepos (&DECL_SIZE (decl), seq_p);
gimplify_one_sizepos (&DECL_SIZE_UNIT (decl), seq_p);
/* Don't mess with a DECL_VALUE_EXPR set by the front-end. */
if (DECL_HAS_VALUE_EXPR_P (decl))
return;
/* All occurrences of this decl in final gimplified code will be
replaced by indirection. Setting DECL_VALUE_EXPR does two
things: First, it lets the rest of the gimplifier know what

View File

@ -0,0 +1,15 @@
// N3639 allows initialization and capture of VLAs
// { dg-options -std=c++1y }
// { dg-do run }
void f(int n)
{
int ar[n] = { 42 };
auto l = [&] { return ar[0]; };
if (l() != 42) __builtin_abort ();
}
int main()
{
f(1);
}

View File

@ -0,0 +1,30 @@
// Test for throwing bad_array_length on invalid array length
// { dg-options -std=c++1y }
// { dg-do run }
#include <new>
int f(int i)
{
int ar[i]{1,2,3,4};
return ar[i-1];
}
void g(int i)
{
int ar[i];
ar[0] = 42;
}
int main()
{
int ok = 0;
f(4); // OK
try { f(3); } // too small
catch (std::bad_array_length) { ++ok; }
try { g(-24); } // negative
catch (std::bad_array_length) { ++ok; }
if (ok != 2)
__builtin_abort ();
}

View File

@ -0,0 +1,24 @@
// Test for range-based for with VLAs.
// { dg-options -std=c++1y }
// { dg-do run }
#include <new>
void f(int i)
{
int ar[i];
int j = 0;
for (int& x : ar)
x = ++j;
[&ar]{
int k = 0;
for (int x : ar)
if (x != ++k)
__builtin_abort();
}();
}
int main()
{
f(42); // OK
}

View File

@ -1,7 +1,7 @@
// PR c++/29175
// { dg-options "" }
// { dg-options "-Wno-vla" }
void foo(int i)
{
int x[][i] = { 0 }; // { dg-error "variable-sized|storage size" }
int x[][i] = { 0 };
}

View File

@ -1,3 +1,13 @@
2013-05-08 Jason Merrill <jason@redhat.com>
Add std::bad_array_length (N3639)
* libsupc++/new: Add std::bad_array_length.
* libsupc++/bad_array_length.cc: New.
* libsupc++/eh_aux_runtime.cc: Add __cxa_bad_array_length.
* libsupc++/Makefile.in: Build them.
* config/abi/pre/gnu.ver: Add new symbols.
* config/abi/pre/gnu-versioned-namespace.ver: Add new symbols.
2013-05-08 Andi Kleen <ak@linux.intel.com>
PR target/55947

View File

@ -235,6 +235,9 @@ CXXABI_2.0 {
__cxa_throw_bad_array_new_length;
_Z*St20bad_array_new_length*;
__cxa_throw_bad_array_length;
_Z*St16bad_array_length*;
# Default function.
_ZSt11_Hash_bytesPKv*;

View File

@ -1559,6 +1559,9 @@ CXXABI_1.3.7 {
CXXABI_1.3.8 {
__cxa_throw_bad_array_new_length;
_Z*St20bad_array_new_length*;
__cxa_throw_bad_array_length;
_Z*St16bad_array_length*;
} CXXABI_1.3.7;
# Symbols in the support library (libsupc++) supporting transactional memory.

View File

@ -92,8 +92,8 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(bitsdir)" \
LTLIBRARIES = $(noinst_LTLIBRARIES) $(toolexeclib_LTLIBRARIES)
libsupc___la_LIBADD =
am__objects_1 = array_type_info.lo atexit_arm.lo atexit_thread.lo \
bad_alloc.lo bad_array_new.lo bad_cast.lo bad_typeid.lo \
class_type_info.lo \
bad_alloc.lo bad_array_length.lo bad_array_new.lo bad_cast.lo \
bad_typeid.lo class_type_info.lo \
del_op.lo del_opnt.lo del_opv.lo del_opvnt.lo dyncast.lo \
eh_alloc.lo eh_arm.lo eh_aux_runtime.lo eh_call.lo eh_catch.lo \
eh_exception.lo eh_globals.lo eh_personality.lo eh_ptr.lo \
@ -367,6 +367,7 @@ sources = \
atexit_arm.cc \
atexit_thread.cc \
bad_alloc.cc \
bad_array_length.cc \
bad_array_new.cc \
bad_cast.cc \
bad_typeid.cc \
@ -789,16 +790,22 @@ cp-demangle.lo: cp-demangle.c
cp-demangle.o: cp-demangle.c
$(C_COMPILE) -DIN_GLIBCPP_V3 -Wno-error -c $<
# Use special rules for the C++11 sources so that the proper flags are passed.
# Use special rules for the C++11 and C++1y sources so that the proper
# flags are passed.
bad_array_length.lo: bad_array_length.cc
$(LTCXXCOMPILE) -std=gnu++1y -c $<
bad_array_length.o: bad_array_length.cc
$(CXXCOMPILE) -std=gnu++1y -c $<
bad_array_new.lo: bad_array_new.cc
$(LTCXXCOMPILE) -std=gnu++11 -c $<
bad_array_new.o: bad_array_new.cc
$(CXXCOMPILE) -std=gnu++11 -c $<
eh_aux_runtime.lo: eh_aux_runtime.cc
$(LTCXXCOMPILE) -std=gnu++11 -c $<
$(LTCXXCOMPILE) -std=gnu++1y -c $<
eh_aux_runtime.o: eh_aux_runtime.cc
$(CXXCOMPILE) -std=gnu++11 -c $<
$(CXXCOMPILE) -std=gnu++1y -c $<
eh_ptr.lo: eh_ptr.cc
$(LTCXXCOMPILE) -std=gnu++11 -c $<

View File

@ -0,0 +1,36 @@
// Copyright (C) 2013 Free Software Foundation, Inc.
//
// This file is part of GCC.
//
// GCC is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3, or (at your option)
// any later version.
// GCC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
#include <new>
namespace std {
bad_array_length::~bad_array_length() _GLIBCXX_USE_NOEXCEPT { }
const char*
bad_array_length::what() const _GLIBCXX_USE_NOEXCEPT
{
return "std::bad_array_length";
}
} // namespace std

View File

@ -154,6 +154,8 @@ namespace __cxxabiv1
void
__cxa_throw_bad_array_new_length() __attribute__((__noreturn__));
void
__cxa_throw_bad_array_length() __attribute__((__noreturn__));
/**
* @brief Demangling routine.

View File

@ -40,3 +40,7 @@ __cxxabiv1::__cxa_bad_typeid ()
extern "C" void
__cxxabiv1::__cxa_throw_bad_array_new_length ()
{ _GLIBCXX_THROW_OR_ABORT(std::bad_array_new_length()); }
extern "C" void
__cxxabiv1::__cxa_throw_bad_array_length ()
{ _GLIBCXX_THROW_OR_ABORT(std::bad_array_length()); }

View File

@ -79,6 +79,23 @@ namespace std
};
#endif
// We throw this exception for GNU VLAs of negative length in all C++
// dialects, so declare it if we aren't in strict conformance mode.
#if __cplusplus > 201103L || !defined(__STRICT_ANSI__)
class bad_array_length : public bad_alloc
{
public:
bad_array_length() throw() { };
// This declaration is not useless:
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
virtual ~bad_array_length() throw();
// See comment in eh_exception.cc.
virtual const char* what() const throw();
};
#endif
struct nothrow_t { };
extern const nothrow_t nothrow;