From patchwork Mon Nov 1 02:29:48 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 69748 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id AD27EB70A5 for ; Mon, 1 Nov 2010 13:30:45 +1100 (EST) Received: (qmail 10828 invoked by alias); 1 Nov 2010 02:30:42 -0000 Received: (qmail 10803 invoked by uid 22791); 1 Nov 2010 02:30:22 -0000 X-SWARE-Spam-Status: No, hits=-1.8 required=5.0 tests=AWL, BAYES_50, KAM_STOCKTIP, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, TW_BF, TW_CX, TW_FS, TW_RG, TW_TM, TW_VP, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 01 Nov 2010 02:29:52 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id oA12ToU8029619 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Sun, 31 Oct 2010 22:29:51 -0400 Received: from [127.0.0.1] (ovpn-113-33.phx2.redhat.com [10.3.113.33]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id oA12TmNh030286 for ; Sun, 31 Oct 2010 22:29:49 -0400 Message-ID: <4CCE261C.1020709@redhat.com> Date: Sun, 31 Oct 2010 22:29:48 -0400 From: Jason Merrill User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.14) Gecko/20101020 Lightning/1.0b1 Shredder/3.0.10pre MIME-Version: 1.0 To: gcc-patches List Subject: C++0x constexpr PATCHes #7-10: the rest of the compiler support Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org The rest of these patches I'm not applying immediately because the last one depends on my fold_indirect_ref_1 patch, and it doesn't really make sense to apply the others without the last one; I'm just sending them to the list now to make the end of Stage 1. decl_constant_var.patch: Replaces uses of DECL_INTEGRAL_CONSTANT_VAR_P with the new decl_constant_var_p which also allows constexpr variables, adjusts when-used template instantiation to require immediate instantiation of constexpr functions. constexpr-register.patch: Handles storing the definition of constexpr functions for later expansion. constexpr-eval.patch: Handles the actual compile-time evaluation of C++0x constant expressions. constexpr-integrate.patch: Integrates the C++0x constant expression semantics into the rest of the compiler. The last patch in particular still needs some work; in particular, I still need to sort out the relationship between constant expression evaluation and value_dependent_expression_p. I think this will just mean using potential_constant_expression, but it still needs to happen; I think this can be cleaned up in stage 3. The other remaining cleanup is to switch the C++ front end over to a c_fully_fold type model of delayed folding; I had originally expected to do that early in development, but it turned out to be more complicated than I expected, so I decided to finish the functionality first. This will be necessary to be fully correct in deciding what is or is not a constant expression. I do not plan to do this for GCC 4.6. Tested x86_64-pc-linux-gnu, not applied quite yet. commit e1c3c7a78bb1d0b58389ba15b13871d2da9c05a9 Author: Jason Merrill Date: Tue Oct 26 19:03:56 2010 -0400 * decl2.c (decl_constant_var_p): New fn. (decl_maybe_constant_var_p): New fn. (mark_used): Rework instantiation of things needed for constant expressions. * cp-tree.h: Declare new fns. * pt.c (instantiate_decl): Handle cp_unevaluated_operand. (always_instantiate_p): Use decl_maybe_constant_var_p. (instantiate_decl): Don't defer constexpr functions. * repo.c (repo_emit_p): Use decl_maybe_constant_var_p. * semantics.c (finish_id_expression): Use decl_constant_var_p. Check for valid name in constant expr after mark_used. commit ba4bc45580a9877d5cb04c6ef2a492c7c70c6a2e Author: Jason Merrill Date: Wed Oct 27 00:22:59 2010 -0400 * cp-tree.h (register_constexpr_fundef): Declare. * decl.c (maybe_save_function_definition): New. (finish_function): Use it. * semantics.c (constexpr_fundef): New datatype. (constexpr_fundef_table): New global table. (constexpr_fundef_equal): New. (constexpr_fundef_hash): Likewise. (retrieve_constexpr_fundef): Likewise. (validate_constexpr_fundecl): Store in the table. (build_data_member_initialization): New fn. (build_constexpr_constructor_member_initializers): New. (register_constexpr_fundef): Define. (is_this_parameter): New. (get_function_named_in_call): Likewise. (get_nth_callarg): Likewise. (check_automatic_or_tls): New. (morally_constexpr_builtin_function_p): New. (potential_constant_expression): New. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index bc4eb46..4dfa8e9 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5252,6 +5252,7 @@ extern void finish_handler (tree); extern void finish_cleanup (tree, tree); extern bool literal_type_p (tree); extern tree validate_constexpr_fundecl (tree); +extern tree register_constexpr_fundef (tree, tree); extern tree ensure_literal_type_for_constexpr_object (tree); enum { diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index b7ab080f..de6f0c4 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -12667,6 +12667,19 @@ record_key_method_defined (tree fndecl) } } +/* Subroutine of finish_function. + Save the body of constexpr functions for possible + future compile time evaluation. */ + +static void +maybe_save_function_definition (tree fun) +{ + if (!processing_template_decl + && DECL_DECLARED_CONSTEXPR_P (fun) + && !DECL_CLONED_FUNCTION_P (fun)) + register_constexpr_fundef (fun, DECL_SAVED_TREE (fun)); +} + /* Finish up a function declaration and compile that function all the way to assembler language output. The free the storage for the function definition. @@ -12778,6 +12791,10 @@ finish_function (int flags) of curly braces for a function. */ gcc_assert (stmts_are_full_exprs_p ()); + /* Save constexpr function body before it gets munged by + the NRV transformation. */ + maybe_save_function_definition (fndecl); + /* Set up the named return value optimization, if we can. Candidate variables are selected in check_return_expr. */ if (current_function_return_value) diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index ad26abb..0420d37 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -5282,6 +5282,55 @@ ensure_literal_type_for_constexpr_object (tree decl) return decl; } +/* Representation of entries in the constexpr function definition table. */ + +typedef struct GTY(()) constexpr_fundef { + tree decl; + tree body; +} constexpr_fundef; + +/* This table holds all constexpr function definitions seen in + the current translation unit. */ + +static GTY ((param_is (constexpr_fundef))) htab_t constexpr_fundef_table; + +static bool potential_constant_expression (tree, tsubst_flags_t); + +/* Utility function used for managing the constexpr function table. + Return true if the entries pointed to by P and Q are for the + same constexpr function. */ + +static inline int +constexpr_fundef_equal (const void *p, const void *q) +{ + const constexpr_fundef *lhs = (const constexpr_fundef *) p; + const constexpr_fundef *rhs = (const constexpr_fundef *) q; + return lhs->decl == rhs->decl; +} + +/* Utility function used for managing the constexpr function table. + Return a hash value for the entry pointed to by Q. */ + +static inline hashval_t +constexpr_fundef_hash (const void *p) +{ + const constexpr_fundef *fundef = (const constexpr_fundef *) p; + return DECL_UID (fundef->decl); +} + +/* Return a previously saved definition of function FUN. */ + +static constexpr_fundef * +retrieve_constexpr_fundef (tree fun) +{ + constexpr_fundef fundef = { NULL, NULL }; + if (constexpr_fundef_table == NULL) + return NULL; + + fundef.decl = fun; + return (constexpr_fundef *) htab_find (constexpr_fundef_table, &fundef); +} + /* Return true if type expression T is a valid parameter type, or a valid return type, of a constexpr function. */ @@ -5343,6 +5392,9 @@ is_valid_constexpr_fn (tree fun, bool complain) tree validate_constexpr_fundecl (tree fun) { + constexpr_fundef entry; + constexpr_fundef **slot; + if (processing_template_decl || !DECL_DECLARED_CONSTEXPR_P (fun)) return NULL; else if (DECL_CLONED_FUNCTION_P (fun)) @@ -5355,10 +5407,684 @@ validate_constexpr_fundecl (tree fun) return NULL; } + /* Create the constexpr function table if necessary. */ + if (constexpr_fundef_table == NULL) + constexpr_fundef_table = htab_create_ggc (101, + constexpr_fundef_hash, + constexpr_fundef_equal, + ggc_free); + entry.decl = fun; + entry.body = NULL; + slot = (constexpr_fundef **) + htab_find_slot (constexpr_fundef_table, &entry, INSERT); + if (*slot == NULL) + { + *slot = ggc_alloc_constexpr_fundef (); + **slot = entry; + } return fun; } +/* Subroutine of build_constexpr_constructor_member_initializers. + The expression tree T represents a data member initialization + in a (constexpr) constructor definition. Build a pairing of + the data member with its initializer, and prepend that pair + to the existing initialization pair INITS. */ +static bool +build_data_member_initialization (tree t, VEC(constructor_elt,gc) **vec) +{ + tree member, init; + if (TREE_CODE (t) == CLEANUP_POINT_EXPR) + t = TREE_OPERAND (t, 0); + if (TREE_CODE (t) == EXPR_STMT) + t = TREE_OPERAND (t, 0); + if (t == error_mark_node) + return false; + if (TREE_CODE (t) == CLEANUP_STMT) + /* We can't see a CLEANUP_STMT in a constructor for a literal class, + but we can in a constexpr constructor for a non-literal class. Just + ignore it; either all the initialization will be constant, in which + case the cleanup can't run, or it can't be constexpr. */ + return true; + if (TREE_CODE (t) == CONVERT_EXPR) + t = TREE_OPERAND (t, 0); + if (TREE_CODE (t) == INIT_EXPR + || TREE_CODE (t) == MODIFY_EXPR) + { + member = TREE_OPERAND (t, 0); + init = unshare_expr (TREE_OPERAND (t, 1)); + } + else + { + tree memtype; + gcc_assert (TREE_CODE (t) == CALL_EXPR); + member = CALL_EXPR_ARG (t, 0); + memtype = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (member))); + if (TREE_CODE (member) == NOP_EXPR) + { + /* We don't put out anything for an empty base. */ + gcc_assert (is_empty_class (memtype)); + /* But if the constructor used isn't constexpr, leave in the call + so we complain later. */ + if (potential_constant_expression (t, tf_none)) + return true; + } + else + { + gcc_assert (TREE_CODE (member) == ADDR_EXPR); + member = TREE_OPERAND (member, 0); + } + /* We don't use build_cplus_new here because it complains about + abstract bases. T has the wrong type, but + cxx_eval_constant_expression doesn't care. */ + init = unshare_expr (t); + } + if (TREE_CODE (member) == COMPONENT_REF) + member = TREE_OPERAND (member, 1); + CONSTRUCTOR_APPEND_ELT (*vec, member, init); + return true; +} + +/* Build compile-time evalable representations of member-initializer list + for a constexpr constructor. */ + +static tree +build_constexpr_constructor_member_initializers (tree type, tree body) +{ + VEC(constructor_elt,gc) *vec = NULL; + bool ok = true; + if (TREE_CODE (body) == MUST_NOT_THROW_EXPR + || TREE_CODE (body) == EH_SPEC_BLOCK) + body = TREE_OPERAND (body, 0); + if (TREE_CODE (body) == BIND_EXPR) + body = BIND_EXPR_BODY (body); + if (TREE_CODE (body) == CLEANUP_POINT_EXPR) + ok = build_data_member_initialization (body, &vec); + else + { + tree_stmt_iterator i; + gcc_assert (TREE_CODE (body) == STATEMENT_LIST); + for (i = tsi_start (body); !tsi_end_p (i); tsi_next (&i)) + { + ok = build_data_member_initialization (tsi_stmt (i), &vec); + if (!ok) + break; + } + } + if (ok) + return build_constructor (type, vec); + else + return error_mark_node; +} + +/* We are processing the definition of the constexpr function FUN. + Check that its BODY fulfills the propriate requirements and + enter it in the constexpr function definition table. + For constructor BODY is actually the TREE_LIST of the + member-initializer list. */ + +tree +register_constexpr_fundef (tree fun, tree body) +{ + constexpr_fundef *fundef = retrieve_constexpr_fundef (fun); + gcc_assert (fundef != NULL && fundef->body == NULL); + + if (DECL_CONSTRUCTOR_P (fun)) + body = build_constexpr_constructor_member_initializers + (DECL_CONTEXT (fun), body); + else + { + if (TREE_CODE (body) == BIND_EXPR) + body = BIND_EXPR_BODY (body); + if (TREE_CODE (body) == EH_SPEC_BLOCK) + body = EH_SPEC_STMTS (body); + if (TREE_CODE (body) == MUST_NOT_THROW_EXPR) + body = TREE_OPERAND (body, 0); + if (TREE_CODE (body) == CLEANUP_POINT_EXPR) + body = TREE_OPERAND (body, 0); + if (TREE_CODE (body) != RETURN_EXPR) + { + error ("body of constexpr function %qD not a return-statement", fun); + DECL_DECLARED_CONSTEXPR_P (fun) = false; + return NULL; + } + body = unshare_expr (TREE_OPERAND (body, 0)); + } + + if (!potential_constant_expression (body, (DECL_TEMPLATE_INSTANTIATION (fun) + ? tf_none : tf_error))) + { + DECL_DECLARED_CONSTEXPR_P (fun) = false; + return NULL; + } + fundef->body = body; + return fun; +} + +/* Return true if T designates the implied `this' parameter. */ + +static inline bool +is_this_parameter (tree t) +{ + return t == current_class_ptr; +} + +/* We have an expression tree T that represents a call, either CALL_EXPR + or AGGR_INIT_EXPR. If the call is lexically to a named function, + retrun the _DECL for that function. */ + +static tree +get_function_named_in_call (tree t) +{ + tree fun = NULL; + switch (TREE_CODE (t)) + { + case CALL_EXPR: + fun = CALL_EXPR_FN (t); + break; + + case AGGR_INIT_EXPR: + fun = AGGR_INIT_EXPR_FN (t); + break; + + default: + gcc_unreachable(); + break; + } + if (TREE_CODE (fun) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (fun, 0)) == FUNCTION_DECL) + fun = TREE_OPERAND (fun, 0); + return fun; +} + +/* We have an expression tree T that represents a call, either CALL_EXPR + or AGGR_INIT_EXPR. Return the Nth argument. */ + +static inline tree +get_nth_callarg (tree t, int n) +{ + switch (TREE_CODE (t)) + { + case CALL_EXPR: + return CALL_EXPR_ARG (t, n); + + case AGGR_INIT_EXPR: + return AGGR_INIT_EXPR_ARG (t, n); + + default: + gcc_unreachable (); + return NULL; + } +} + +/* Return true if the object referred to by REF has automatic or thread + local storage. */ + +enum { ck_ok, ck_bad, ck_unknown }; +static int +check_automatic_or_tls (tree ref) +{ + enum machine_mode mode; + HOST_WIDE_INT bitsize, bitpos; + tree offset; + int volatilep = 0, unsignedp = 0; + tree decl = get_inner_reference (ref, &bitsize, &bitpos, &offset, + &mode, &unsignedp, &volatilep, false); + duration_kind dk; + + /* If there isn't a decl in the middle, we don't know the linkage here, + and this isn't a constant expression anyway. */ + if (!DECL_P (decl)) + return ck_unknown; + dk = decl_storage_duration (decl); + return (dk == dk_auto || dk == dk_thread) ? ck_bad : ck_ok; +} + +/* Return true if the DECL designates a builtin function that is + morally constexpr, in the sense that its parameter types and + return type are literal types and the compiler is allowed to + fold its invocations. */ + +static bool +morally_constexpr_builtin_function_p (tree decl) +{ + tree funtype = TREE_TYPE (decl); + tree t; + + if (!is_builtin_fn (decl)) + return false; + if (!literal_type_p (TREE_TYPE (funtype))) + return false; + for (t = TYPE_ARG_TYPES (funtype); t != NULL ; t = TREE_CHAIN (t)) + { + if (t == void_list_node) + return true; + if (!literal_type_p (TREE_VALUE (t))) + return false; + } + /* We assume no varargs builtins are suitable. */ + return t != NULL; +} + +/* Return true if T denotes a constant expression, or potential constant + expression if POTENTIAL is true. + Issue diagnostic as appropriate under control of flags. Variables + with static storage duration initialized by constant expressions + are guaranteed to be statically initialized. + + C++0x [expr.const] + + 6 An expression is a potential constant expression if it is + a constant expression where all occurences of function + parameters are replaced by arbitrary constant expressions + of the appropriate type. + + 2 A conditional expression is a constant expression unless it + involves one of the following as a potentially evaluated + subexpression (3.2), but subexpressions of logical AND (5.14), + logical OR (5.15), and conditional (5.16) operations that are + not evaluated are not considered. */ + +static bool +potential_constant_expression (tree t, tsubst_flags_t flags) +{ + int i; + tree tmp; + if (t == error_mark_node) + return false; + if (TREE_THIS_VOLATILE (t)) + { + if (flags & tf_error) + error ("expression %qE has side-effects", t); + return false; + } + if (CONSTANT_CLASS_P (t)) + return true; + + switch (TREE_CODE (t)) + { + case FUNCTION_DECL: + case LABEL_DECL: + case CONST_DECL: + return true; + + case PARM_DECL: + /* -- this (5.1) unless it appears as the postfix-expression in a + class member access expression, including the result of the + implicit transformation in the body of the non-static + member function (9.3.1); */ + if (is_this_parameter (t)) + { + if (flags & tf_error) + error ("%qE is not a potential constant expression", t); + return false; + } + return true; + + case AGGR_INIT_EXPR: + case CALL_EXPR: + /* -- an invocation of a function other than a constexpr function + or a constexpr constructor. */ + { + tree fun = get_function_named_in_call (t); + const int nargs = call_expr_nargs (t); + if (TREE_CODE (fun) != FUNCTION_DECL) + { + if (potential_constant_expression (fun, flags)) + /* Might end up being a constant function pointer. */ + return true; + if (flags & tf_error) + error ("%qE is not a function name", fun); + return false; + } + /* Skip initial arguments to base constructors. */ + if (DECL_BASE_CONSTRUCTOR_P (fun)) + i = num_artificial_parms_for (fun); + else + i = 0; + fun = DECL_ORIGIN (fun); + if (builtin_valid_in_constant_expr_p (fun)) + return true; + if (!DECL_DECLARED_CONSTEXPR_P (fun) + && !morally_constexpr_builtin_function_p (fun)) + { + if (flags & tf_error) + error ("%qD is not %", fun); + return false; + } + for (; i < nargs; ++i) + { + tree x = get_nth_callarg (t, i); + /* A call to a non-static member function takes the + address of the object as the first argument. + But in a constant expression the address will be folded + away, so look through it now. */ + if (i == 0 && DECL_NONSTATIC_MEMBER_P (fun) + && !DECL_CONSTRUCTOR_P (fun)) + { + if (TREE_CODE (x) == ADDR_EXPR) + x = TREE_OPERAND (x, 0); + if (is_this_parameter (x)) + /* OK. */; + else if (!potential_constant_expression (x, flags)) + { + if (flags & tf_error) + error ("object argument is not a potential constant " + "expression"); + return false; + } + } + else if (!potential_constant_expression (x, flags)) + { + if (flags & tf_error) + error ("argument in position %qP is not a " + "potential constant expression", i); + return false; + } + } + return true; + } + + case NON_LVALUE_EXPR: + /* -- an lvalue-to-rvalue conversion (4.1) unless it is applied to + -- an lvalue of integral type that refers to a non-volatile + const variable or static data member initialized with + constant expressions, or + + -- an lvalue of literal type that refers to non-volatile + object defined with constexpr, or that refers to a + sub-object of such an object; */ + return potential_constant_expression (TREE_OPERAND (t, 0), flags); + + case VAR_DECL: + if (!decl_constant_var_p (t)) + { + if (flags & tf_error) + error ("variable %qD is not declared constexpr", t); + return false; + } + return true; + + case NOP_EXPR: + case CONVERT_EXPR: + case VIEW_CONVERT_EXPR: + /* -- an array-to-pointer conversion that is applied to an lvalue + that designates an object with thread or automatic storage + duration; FIXME not implemented as it breaks constexpr arrays; + need to fix the standard + -- a type conversion from a pointer or pointer-to-member type + to a literal type. */ + { + tree from = TREE_OPERAND (t, 0); + tree source = TREE_TYPE (from); + tree target = TREE_TYPE (t); + if (TYPE_PTR_P (source) && ARITHMETIC_TYPE_P (target) + && !(TREE_CODE (from) == COMPONENT_REF + && TYPE_PTRMEMFUNC_P (TREE_TYPE (TREE_OPERAND (from, 0))))) + { + if (flags & tf_error) + error ("conversion of expression %qE of pointer type " + "cannot yield a constant expression", from); + return false; + } + return potential_constant_expression (from, flags); + } + + case ADDR_EXPR: + /* -- a unary operator & that is applied to an lvalue that + designates an object with thread or automatic storage + duration; */ + t = TREE_OPERAND (t, 0); + i = check_automatic_or_tls (t); + if (i == ck_ok) + return true; + if (i == ck_bad) + { + if (flags & tf_error) + error ("address-of an object %qE with thread local or " + "automatic storage is not a constant expression", t); + return false; + } + return potential_constant_expression (t, flags); + + case COMPONENT_REF: + case BIT_FIELD_REF: + /* -- a class member access unless its postfix-expression is + of literal type or of pointer to literal type. */ + /* This test would be redundant, as it follows from the + postfix-expression being a potential constant expression. */ + return potential_constant_expression (TREE_OPERAND (t, 0), flags); + + case INDIRECT_REF: + { + tree x = TREE_OPERAND (t, 0); + STRIP_NOPS (x); + if (is_this_parameter (x)) + return true; + return potential_constant_expression (x, flags); + } + + case LAMBDA_EXPR: + case DYNAMIC_CAST_EXPR: + case PSEUDO_DTOR_EXPR: + case PREINCREMENT_EXPR: + case POSTINCREMENT_EXPR: + case PREDECREMENT_EXPR: + case POSTDECREMENT_EXPR: + case NEW_EXPR: + case VEC_NEW_EXPR: + case DELETE_EXPR: + case VEC_DELETE_EXPR: + case THROW_EXPR: + case MODIFY_EXPR: + case MODOP_EXPR: + /* GCC internal stuff. */ + case VA_ARG_EXPR: + case OBJ_TYPE_REF: + case WITH_CLEANUP_EXPR: + case CLEANUP_POINT_EXPR: + case MUST_NOT_THROW_EXPR: + case TRY_CATCH_EXPR: + case STATEMENT_LIST: + case BIND_EXPR: + if (flags & tf_error) + error ("expression %qE is not a constant-expression", t); + return false; + + case TYPEID_EXPR: + /* -- a typeid expression whose operand is of polymorphic + class type; */ + { + tree e = TREE_OPERAND (t, 0); + if (!TYPE_P (e) && TYPE_POLYMORPHIC_P (TREE_TYPE (e))) + { + if (flags & tf_error) + error ("typeid-expression is not a constant expression " + "because %qE is of polymorphic type", e); + return false; + } + return true; + } + + case MINUS_EXPR: + /* -- a subtraction where both operands are pointers. */ + if (TYPE_PTR_P (TREE_OPERAND (t, 0)) + && TYPE_PTR_P (TREE_OPERAND (t, 1))) + { + if (flags & tf_error) + error ("difference of two pointer expressions is not " + "a constant expression"); + return false; + } + goto binary; + + case LT_EXPR: + case LE_EXPR: + case GT_EXPR: + case GE_EXPR: + case EQ_EXPR: + case NE_EXPR: + /* -- a relational or equality operator where at least + one of the operands is a pointer. */ + if (TYPE_PTR_P (TREE_OPERAND (t, 0)) + || TYPE_PTR_P (TREE_OPERAND (t, 1))) + { + if (flags & tf_error) + error ("pointer comparison expression is not a " + "constant expression"); + return false; + } + goto binary; + + case REALPART_EXPR: + case IMAGPART_EXPR: + case CONJ_EXPR: + case SAVE_EXPR: + case FIX_TRUNC_EXPR: + case FLOAT_EXPR: + case NEGATE_EXPR: + case ABS_EXPR: + case BIT_NOT_EXPR: + case TRUTH_NOT_EXPR: + case PAREN_EXPR: + case FIXED_CONVERT_EXPR: + /* For convenience. */ + case RETURN_EXPR: + return potential_constant_expression (TREE_OPERAND (t, 0), flags); + + case INIT_EXPR: + case TARGET_EXPR: + return potential_constant_expression (TREE_OPERAND (t, 1), flags); + + case CONSTRUCTOR: + { + VEC(constructor_elt, gc) *v = CONSTRUCTOR_ELTS (t); + constructor_elt *ce; + for (i = 0; VEC_iterate (constructor_elt, v, i, ce); ++i) + if (!potential_constant_expression (ce->value, flags)) + return false; + return true; + } + + case TREE_LIST: + { + gcc_assert (TREE_PURPOSE (t) == NULL_TREE + || DECL_P (TREE_PURPOSE (t))); + if (!potential_constant_expression (TREE_VALUE (t), flags)) + return false; + if (TREE_CHAIN (t) == NULL_TREE) + return true; + return potential_constant_expression (TREE_CHAIN (t), flags); + } + + case TRUNC_DIV_EXPR: + case CEIL_DIV_EXPR: + case FLOOR_DIV_EXPR: + case ROUND_DIV_EXPR: + case TRUNC_MOD_EXPR: + case CEIL_MOD_EXPR: + case ROUND_MOD_EXPR: + if (integer_zerop (decl_constant_value (TREE_OPERAND (t, 1)))) + return false; + else + goto binary; + + case COMPOUND_EXPR: + { + /* check_return_expr sometimes wraps a TARGET_EXPR in a + COMPOUND_EXPR; don't get confused. Also handle EMPTY_CLASS_EXPR + introduced by build_call_a. */ + tree op0 = TREE_OPERAND (t, 0); + tree op1 = TREE_OPERAND (t, 1); + STRIP_NOPS (op1); + if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0)) + || TREE_CODE (op1) == EMPTY_CLASS_EXPR) + return potential_constant_expression (op0, flags); + else + goto binary; + } + + /* If the first operand is the non-short-circuit constant, look at + the second operand; otherwise we only care about the first one for + potentiality. */ + case TRUTH_AND_EXPR: + case TRUTH_ANDIF_EXPR: + tmp = boolean_true_node; + goto truth; + case TRUTH_OR_EXPR: + case TRUTH_ORIF_EXPR: + tmp = boolean_false_node; + truth: + if (TREE_OPERAND (t, 0) == tmp) + return potential_constant_expression (TREE_OPERAND (t, 1), flags); + else + return potential_constant_expression (TREE_OPERAND (t, 0), flags); + + case ARRAY_REF: + case ARRAY_RANGE_REF: + case PLUS_EXPR: + case MULT_EXPR: + case POINTER_PLUS_EXPR: + case RDIV_EXPR: + case EXACT_DIV_EXPR: + case MIN_EXPR: + case MAX_EXPR: + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case BIT_AND_EXPR: + case UNLT_EXPR: + case UNLE_EXPR: + case UNGT_EXPR: + case UNGE_EXPR: + case UNEQ_EXPR: + case RANGE_EXPR: + case COMPLEX_EXPR: + binary: + for (i = 0; i < 2; ++i) + if (!potential_constant_expression (TREE_OPERAND (t, i), + flags)) + return false; + return true; + + case COND_EXPR: + case VEC_COND_EXPR: + /* If the condition is a known constant, we know which of the legs we + care about; otherwise we only require that the condition and + either of the legs be potentially constant. */ + tmp = TREE_OPERAND (t, 0); + if (!potential_constant_expression (tmp, flags)) + return false; + else if (tmp == boolean_true_node) + return potential_constant_expression (TREE_OPERAND (t, 1), flags); + else if (tmp == boolean_false_node) + return potential_constant_expression (TREE_OPERAND (t, 2), flags); + for (i = 1; i < 3; ++i) + if (potential_constant_expression (TREE_OPERAND (t, i), tf_none)) + return true; + if (flags & tf_error) + error ("expression %qE is not a constant-expression", t); + return false; + + case VEC_INIT_EXPR: + /* We should only see this in a defaulted constructor for a class + with a non-static data member of array type; if we get here we + know this is a potential constant expression. */ + gcc_assert (DECL_DEFAULTED_FN (current_function_decl)); + return true; + + default: + sorry ("unexpected ast of kind %s", tree_code_name[TREE_CODE (t)]); + gcc_unreachable(); + return false; + } +} + + /* Constructor for a lambda expression. */ tree diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C new file mode 100644 index 0000000..5280b13 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C @@ -0,0 +1,11 @@ +// { dg-options -std=c++0x } + +struct A +{ + A(); +}; + +struct B : A +{ + constexpr B(): A() { } // { dg-error "A::A" } +}; commit 18599f6788f8dca4f11717e81bd3ea5cf152c0d0 Author: Jason Merrill Date: Tue Oct 26 21:37:55 2010 -0400 * semantics.c (constexpr_call): New datatype. (constexpr_call_table): New global table. (constexpr_call_hash): New. (constexpr_call_equal): Likewise. (maybe_initialize_constexpr_call_table): Likewise. (lookup_parameter_binding): Likewise. (cxx_eval_builtin_function_call): Likewise. (cxx_bind_parameters_in_call): Likewise. (cxx_eval_call_expression): Likewise. (cxx_eval_unary_expression): Likewise. (cxx_eval_binary_expression): Likewise. (cxx_eval_conditional_expression): Likewise. (cxx_eval_array_reference): Likewise. (cxx_eval_component_reference): Likewise. (cxx_eval_logical_expression): Likewise. (cxx_eval_object_construction): Likewise. (cxx_eval_constant_expression): Likewise. (cxx_eval_indirect_ref): Likewise. (cxx_constant_value): Likewise. (cxx_eval_bare_aggregate): Likewise. (adjust_temp_type): New. (reduced_constant_expression_p): New. (verify_constant): New. (cxx_eval_vec_init, cxx_eval_vec_init_1): New. (cxx_eval_outermost_constant_expr): New. (maybe_constant_value, maybe_constant_init): New. (cxx_eval_constant_expression): Use them. * cp-tree.h: Declare fns. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 4dfa8e9..96fefae 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5122,6 +5122,7 @@ extern tree get_template_innermost_arguments (const_tree); extern tree get_template_argument_pack_elems (const_tree); extern tree get_function_template_decl (const_tree); extern tree resolve_nondeduced_context (tree); +extern hashval_t iterative_hash_template_arg (tree arg, hashval_t val); /* in repo.c */ extern void init_repo (void); @@ -5254,6 +5255,10 @@ extern bool literal_type_p (tree); extern tree validate_constexpr_fundecl (tree); extern tree register_constexpr_fundef (tree, tree); 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 reduced_constant_expression_p (tree); enum { BCS_NO_SCOPE = 1, diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 0047aae..9a72ea8 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -187,7 +187,6 @@ static tree tsubst_decl (tree, tree, tsubst_flags_t); static void perform_typedefs_access_check (tree tmpl, tree targs); static void append_type_to_template_for_access_check_1 (tree, tree, tree, location_t); -static hashval_t iterative_hash_template_arg (tree arg, hashval_t val); static tree listify (tree); static tree listify_autos (tree, tree); @@ -1458,7 +1457,7 @@ hash_specialization (const void *p) /* Recursively calculate a hash value for a template argument ARG, for use in the hash tables of template specializations. */ -static hashval_t +hashval_t iterative_hash_template_arg (tree arg, hashval_t val) { unsigned HOST_WIDE_INT i; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 0420d37..039f0a5 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -5562,6 +5562,89 @@ register_constexpr_fundef (tree fun, tree body) return fun; } +/* Objects of this type represent calls to constexpr functions + along with the bindings of parameters to their arguments, for + the purpose of compile time evaluation. */ + +typedef struct GTY(()) constexpr_call { + /* Description of the constexpr function definition. */ + constexpr_fundef *fundef; + /* Parameter bindings enironment. A TREE_LIST where each TREE_PURPOSE + is a parameter _DECL and the TREE_VALUE is the value of the parameter. + Note: This arrangement is made to accomodate the use of + iterative_hash_template_arg (see pt.c). If you change this + representation, also change the hash calculation in + cxx_eval_call_expression. */ + tree bindings; + /* Result of the call. + NULL means the call is being evaluated. + error_mark_node means that the evaluation was erroneous; + otherwise, the actuall value of the call. */ + tree result; + /* The hash of this call; we remember it here to avoid having to + recalculate it when expanding the hash table. */ + hashval_t hash; +} constexpr_call; + +/* A table of all constexpr calls that have been evaluated by the + compiler in this translation unit. */ + +static GTY ((param_is (constexpr_call))) htab_t constexpr_call_table; + +static tree cxx_eval_constant_expression (const constexpr_call *, tree, + bool, bool, bool *); + +/* Compute a hash value for a constexpr call representation. */ + +static hashval_t +constexpr_call_hash (const void *p) +{ + const constexpr_call *info = (const constexpr_call *) p; + return info->hash; +} + +/* Return 1 if the objects pointed to by P and Q represent calls + to the same constexpr function with the same arguments. + Otherwise, return 0. */ + +static int +constexpr_call_equal (const void *p, const void *q) +{ + const constexpr_call *lhs = (const constexpr_call *) p; + const constexpr_call *rhs = (const constexpr_call *) q; + tree lhs_bindings; + tree rhs_bindings; + if (lhs == rhs) + return 1; + if (!constexpr_fundef_equal (lhs->fundef, rhs->fundef)) + return 0; + lhs_bindings = lhs->bindings; + rhs_bindings = rhs->bindings; + while (lhs_bindings != NULL && rhs_bindings != NULL) + { + tree lhs_arg = TREE_VALUE (lhs_bindings); + tree rhs_arg = TREE_VALUE (rhs_bindings); + gcc_assert (TREE_TYPE (lhs_arg) == TREE_TYPE (rhs_arg)); + if (!cp_tree_equal (lhs_arg, rhs_arg)) + return 0; + lhs_bindings = TREE_CHAIN (lhs_bindings); + rhs_bindings = TREE_CHAIN (rhs_bindings); + } + return lhs_bindings == rhs_bindings; +} + +/* Initialize the constexpr call table, if needed. */ + +static void +maybe_initialize_constexpr_call_table (void) +{ + if (constexpr_call_table == NULL) + constexpr_call_table = htab_create_ggc (101, + constexpr_call_hash, + constexpr_call_equal, + ggc_free); +} + /* Return true if T designates the implied `this' parameter. */ static inline bool @@ -5618,6 +5701,1195 @@ get_nth_callarg (tree t, int n) } } +/* Look up the binding of the function parameter T in a constexpr + function call context CALL. */ + +static tree +lookup_parameter_binding (const constexpr_call *call, tree t) +{ + tree b = purpose_member (t, call->bindings); + return TREE_VALUE (b); +} + +/* Attempt to evaluate T which represents a call to a builtin function. + We assume here that all builtin functions evaluate to scalar types + represented by _CST nodes. */ + +static tree +cxx_eval_builtin_function_call (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + const int nargs = call_expr_nargs (t); + tree *args = (tree *) alloca (nargs * sizeof (tree)); + tree new_call; + int i; + for (i = 0; i < nargs; ++i) + { + args[i] = cxx_eval_constant_expression (call, CALL_EXPR_ARG (t, i), + allow_non_constant, addr, + non_constant_p); + if (allow_non_constant && *non_constant_p) + return t; + } + if (*non_constant_p) + return t; + new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t), + CALL_EXPR_FN (t), nargs, args); + return fold (new_call); +} + +/* TEMP is the constant value of a temporary object of type TYPE. Adjust + the type of the value to match. */ + +static tree +adjust_temp_type (tree type, tree temp) +{ + if (TREE_TYPE (temp) == type) + return temp; + /* Avoid wrapping an aggregate value in a NOP_EXPR. */ + if (TREE_CODE (temp) == CONSTRUCTOR) + return build_constructor (type, CONSTRUCTOR_ELTS (temp)); + gcc_assert (SCALAR_TYPE_P (type)); + return fold_convert (type, temp); +} + +/* Subroutine of cxx_eval_call_expression. + We are processing a call expression (either CALL_EXPR or + AGGR_INIT_EXPR) in the call context of OLD_CALL. Evaluate + all arguments and bind their values to correspondings + parameters, making up the NEW_CALL context. */ + +static void +cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t, + constexpr_call *new_call, + bool allow_non_constant, + bool *non_constant_p) +{ + const int nargs = call_expr_nargs (t); + tree fun = new_call->fundef->decl; + tree parms = DECL_ARGUMENTS (fun); + int i; + for (i = 0; i < nargs; ++i) + { + tree x, arg; + tree type = parms ? TREE_TYPE (parms) : void_type_node; + /* For member function, the first argument is a pointer to the implied + object. And for an object contruction, don't bind `this' before + it is fully constructed. */ + if (i == 0 && DECL_CONSTRUCTOR_P (fun)) + goto next; + x = get_nth_callarg (t, i); + arg = cxx_eval_constant_expression (old_call, x, allow_non_constant, + TREE_CODE (type) == REFERENCE_TYPE, + non_constant_p); + /* Don't VERIFY_CONSTANT here. */ + if (*non_constant_p && allow_non_constant) + return; + /* Just discard ellipsis args after checking their constantitude. */ + if (!parms) + continue; + + /* Make sure the binding has the same type as the parm. */ + if (TREE_CODE (type) != REFERENCE_TYPE) + arg = adjust_temp_type (type, arg); + new_call->bindings = tree_cons (parms, arg, new_call->bindings); + next: + parms = TREE_CHAIN (parms); + } +} + +/* Subroutine of cxx_eval_constant_expression. + Evaluate the call expression tree T in the context of OLD_CALL expression + evaluation. */ + +static tree +cxx_eval_call_expression (const constexpr_call *old_call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + location_t loc = EXPR_LOCATION (t); + tree fun = get_function_named_in_call (t); + tree result; + constexpr_call new_call = { NULL, NULL, NULL, 0 }; + constexpr_call **slot; + if (loc == UNKNOWN_LOCATION) + loc = input_location; + if (TREE_CODE (fun) != FUNCTION_DECL) + { + /* Might be a constexpr function pointer. */ + fun = cxx_eval_constant_expression (old_call, fun, allow_non_constant, + /*addr*/false, non_constant_p); + if (TREE_CODE (fun) == ADDR_EXPR) + fun = TREE_OPERAND (fun, 0); + } + if (TREE_CODE (fun) != FUNCTION_DECL) + { + if (!allow_non_constant) + error_at (loc, "expression %qE does not designate a constexpr " + "function", fun); + *non_constant_p = true; + return t; + } + if (DECL_CLONED_FUNCTION_P (fun)) + fun = DECL_CLONED_FUNCTION (fun); + if (is_builtin_fn (fun)) + return cxx_eval_builtin_function_call (old_call, t, allow_non_constant, + addr, non_constant_p); + if (!DECL_DECLARED_CONSTEXPR_P (fun)) + { + if (!allow_non_constant) + { + error_at (loc, "%qD is not a constexpr function", fun); + if (DECL_TEMPLATE_INSTANTIATION (fun) + && DECL_DECLARED_CONSTEXPR_P (DECL_TEMPLATE_RESULT + (DECL_TI_TEMPLATE (fun)))) + is_valid_constexpr_fn (fun, true); + } + *non_constant_p = true; + return t; + } + + /* If in direct recursive call, optimize definition search. */ + if (old_call != NULL && old_call->fundef->decl == fun) + new_call.fundef = old_call->fundef; + else + { + new_call.fundef = retrieve_constexpr_fundef (fun); + if (new_call.fundef == NULL || new_call.fundef->body == NULL) + { + if (!allow_non_constant) + error_at (loc, "%qD used before its definition", fun); + *non_constant_p = true; + return t; + } + } + cxx_bind_parameters_in_call (old_call, t, &new_call, + allow_non_constant, non_constant_p); + if (*non_constant_p) + return t; + + new_call.hash + = iterative_hash_template_arg (new_call.bindings, + constexpr_fundef_hash (new_call.fundef)); + + /* If we have seen this call before, we are done. */ + maybe_initialize_constexpr_call_table (); + slot = (constexpr_call **) + htab_find_slot (constexpr_call_table, &new_call, INSERT); + if (*slot != NULL) + { + /* Calls which are in progress have their result set to NULL + so that we can detect circular dependencies. */ + if ((*slot)->result == NULL) + { + if (!allow_non_constant) + error ("call has circular dependency"); + (*slot)->result = result = error_mark_node; + } + else + { + result = (*slot)->result; + if (result == error_mark_node && !allow_non_constant) + /* Re-evaluate to get the error. */ + cxx_eval_constant_expression (&new_call, new_call.fundef->body, + allow_non_constant, addr, + non_constant_p); + } + } + else + { + /* We need to keep a pointer to the entry, not just the slot, as the + slot can move in the call to cxx_eval_builtin_function_call. */ + constexpr_call *entry = ggc_alloc_constexpr_call (); + *entry = new_call; + *slot = entry; + result + = cxx_eval_constant_expression (&new_call, new_call.fundef->body, + allow_non_constant, addr, + non_constant_p); + if (*non_constant_p) + entry->result = result = error_mark_node; + else + { + /* If this was a call to initialize an object, set the type of + the CONSTRUCTOR to the type of that object. */ + if (DECL_CONSTRUCTOR_P (fun)) + { + tree ob_arg = get_nth_callarg (t, 0); + STRIP_NOPS (ob_arg); + gcc_assert (TREE_CODE (TREE_TYPE (ob_arg)) == POINTER_TYPE + && CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (ob_arg)))); + result = adjust_temp_type (TREE_TYPE (TREE_TYPE (ob_arg)), + result); + } + entry->result = result; + } + } + + if (result == error_mark_node) + { + if (!allow_non_constant) + error_at (loc, "in expansion of %qE", t); + *non_constant_p = true; + result = t; + } + return result; +} + +bool +reduced_constant_expression_p (tree t) +{ + /* FIXME speed this up, it's taking 16% of compile time on sieve testcase. */ + if (cxx_dialect >= cxx0x && TREE_OVERFLOW_P (t)) + /* In C++0x, integer overflow makes this not a constant expression. + FIXME arithmetic overflow is different from conversion truncation */ + return false; + /* FIXME are we calling this too much? */ + return initializer_constant_valid_p (t, TREE_TYPE (t)) != NULL_TREE; +} + +/* Some expressions may have constant operands but are not constant + themselves, such as 1/0. Call this function (or rather, the macro + following it) to check for that condition. + + We only call this in places that require an arithmetic constant, not in + places where we might have a non-constant expression that can be a + component of a constant expression, such as the address of a constexpr + variable that might be dereferenced later. */ + +static bool +verify_constant (tree t, bool allow_non_constant, bool *non_constant_p) +{ + if (!*non_constant_p && !reduced_constant_expression_p (t)) + { + if (!allow_non_constant) + error ("%qE is not a constant expression", t); + *non_constant_p = true; + } + return *non_constant_p; +} +#define VERIFY_CONSTANT(X) \ +do { \ + if (verify_constant ((X), allow_non_constant, non_constant_p)) \ + return t; \ + } while (0) + +/* Subroutine of cxx_eval_constant_expression. + Attempt to reduce the unary expression tree T to a compile time value. + If successful, return the value. Otherwise issue a diagnostic + and return error_mark_node. */ + +static tree +cxx_eval_unary_expression (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree r; + tree orig_arg = TREE_OPERAND (t, 0); + tree arg = cxx_eval_constant_expression (call, orig_arg, allow_non_constant, + addr, non_constant_p); + VERIFY_CONSTANT (arg); + if (arg == orig_arg) + return t; + r = fold_build1 (TREE_CODE (t), TREE_TYPE (t), arg); + VERIFY_CONSTANT (r); + return r; +} + +/* Subroutine of cxx_eval_constant_expression. + Like cxx_eval_unary_expression, except for binary expressions. */ + +static tree +cxx_eval_binary_expression (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree r; + tree orig_lhs = TREE_OPERAND (t, 0); + tree orig_rhs = TREE_OPERAND (t, 1); + tree lhs, rhs; + lhs = cxx_eval_constant_expression (call, orig_lhs, + allow_non_constant, addr, + non_constant_p); + VERIFY_CONSTANT (lhs); + rhs = cxx_eval_constant_expression (call, orig_rhs, + allow_non_constant, addr, + non_constant_p); + VERIFY_CONSTANT (rhs); + if (lhs == orig_lhs && rhs == orig_rhs) + return t; + r = fold_build2 (TREE_CODE (t), TREE_TYPE (t), lhs, rhs); + VERIFY_CONSTANT (r); + return r; +} + +/* Subroutine of cxx_eval_constant_expression. + Attempt to evaluate condition expressions. Dead branches are not + looked into. */ + +static tree +cxx_eval_conditional_expression (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree val = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), + allow_non_constant, addr, + non_constant_p); + VERIFY_CONSTANT (val); + if (val == boolean_true_node) + return cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), + allow_non_constant, addr, + non_constant_p); + gcc_assert (val == boolean_false_node); + /* Don't VERIFY_CONSTANT here. */ + return cxx_eval_constant_expression (call, TREE_OPERAND (t, 2), + allow_non_constant, addr, + non_constant_p); +} + +/* Subroutine of cxx_eval_constant_expression. + Attempt to reduce a reference to an array slot. */ + +static tree +cxx_eval_array_reference (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree oldary = TREE_OPERAND (t, 0); + tree ary = cxx_eval_constant_expression (call, oldary, + allow_non_constant, addr, + non_constant_p); + tree index, oldidx; + HOST_WIDE_INT i; + unsigned len; + if (*non_constant_p) + return t; + oldidx = TREE_OPERAND (t, 1); + index = cxx_eval_constant_expression (call, oldidx, + allow_non_constant, false, + non_constant_p); + VERIFY_CONSTANT (index); + if (addr && ary == oldary && index == oldidx) + return t; + else if (addr) + return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL); + len = (TREE_CODE (ary) == CONSTRUCTOR + ? CONSTRUCTOR_NELTS (ary) + : (unsigned)TREE_STRING_LENGTH (ary)); + if (compare_tree_int (index, len) >= 0) + { + if (!allow_non_constant) + error ("array subscript out of bound"); + *non_constant_p = true; + return t; + } + i = tree_low_cst (index, 0); + if (TREE_CODE (ary) == CONSTRUCTOR) + return VEC_index (constructor_elt, CONSTRUCTOR_ELTS (ary), i)->value; + else + return build_int_cst (cv_unqualified (TREE_TYPE (TREE_TYPE (ary))), + TREE_STRING_POINTER (ary)[i]); + /* Don't VERIFY_CONSTANT here. */ +} + +/* Subroutine of cxx_eval_constant_expression. + Attempt to reduce a field access of a value of class type. */ + +static tree +cxx_eval_component_reference (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + unsigned HOST_WIDE_INT i; + tree field; + tree value; + tree part = TREE_OPERAND (t, 1); + tree orig_whole = TREE_OPERAND (t, 0); + tree whole = cxx_eval_constant_expression (call, orig_whole, + allow_non_constant, addr, + non_constant_p); + if (whole == orig_whole) + return t; + if (addr) + return fold_build3 (COMPONENT_REF, TREE_TYPE (t), + whole, part, NULL_TREE); + /* Don't VERIFY_CONSTANT here; we only want to check that we got a + CONSTRUCTOR. */ + if (!*non_constant_p && TREE_CODE (whole) != CONSTRUCTOR) + { + if (!allow_non_constant) + error ("%qE is not a constant expression", orig_whole); + *non_constant_p = true; + } + if (*non_constant_p) + return t; + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value) + { + if (field == part) + return value; + } + if (TREE_CODE (TREE_TYPE (whole)) == UNION_TYPE) + { + /* FIXME Mike Miller wants this to be OK. */ + if (!allow_non_constant) + error ("accessing %qD member instead of initialized %qD member in " + "constant expression", part, CONSTRUCTOR_ELT (whole, 0)->index); + *non_constant_p = true; + return t; + } + gcc_unreachable(); + return error_mark_node; +} + +/* Subroutine of cxx_eval_constant_expression. + Evaluate a short-circuited logical expression T in the context + of a given constexpr CALL. BAILOUT_VALUE is the value for + early return. CONTINUE_VALUE is used here purely for + sanity check purposes. */ + +static tree +cxx_eval_logical_expression (const constexpr_call *call, tree t, + tree bailout_value, tree continue_value, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree r; + tree lhs = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), + allow_non_constant, addr, + non_constant_p); + VERIFY_CONSTANT (lhs); + if (lhs == bailout_value) + return lhs; + gcc_assert (lhs == continue_value); + r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), + allow_non_constant, addr, non_constant_p); + VERIFY_CONSTANT (r); + return r; +} + +/* Subroutine of cxx_eval_constant_expression. + The expression tree T denotes a C-style array or a C-style + aggregate. Reduce it to a constant expression. */ + +static tree +cxx_eval_bare_aggregate (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + VEC(constructor_elt,gc) *v = CONSTRUCTOR_ELTS (t); + VEC(constructor_elt,gc) *n = VEC_alloc (constructor_elt, gc, + VEC_length (constructor_elt, v)); + constructor_elt *ce; + HOST_WIDE_INT i; + bool changed = false; + tree type = TREE_TYPE (t); + gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (t)); + for (i = 0; VEC_iterate (constructor_elt, v, i, ce); ++i) + { + tree elt = cxx_eval_constant_expression (call, ce->value, + allow_non_constant, addr, + non_constant_p); + /* Don't VERIFY_CONSTANT here. */ + if (allow_non_constant && *non_constant_p) + goto fail; + if (elt != ce->value) + changed = true; + if (TREE_CODE (type) != ARRAY_TYPE + && !(same_type_ignoring_top_level_qualifiers_p + (DECL_CONTEXT (ce->index), type))) + { + /* Push our vtable pointer down into the base where it belongs. */ + tree vptr_base = DECL_CONTEXT (ce->index); + tree base_ctor; + gcc_assert (ce->index == TYPE_VFIELD (type)); + for (base_ctor = VEC_index (constructor_elt, n, 0)->value; ; + base_ctor = CONSTRUCTOR_ELT (base_ctor, 0)->value) + if (TREE_TYPE (base_ctor) == vptr_base) + { + constructor_elt *p = CONSTRUCTOR_ELT (base_ctor, 0); + gcc_assert (p->index == ce->index); + p->value = elt; + break; + } + } + else + CONSTRUCTOR_APPEND_ELT (n, ce->index, elt); + } + if (*non_constant_p || !changed) + { + fail: + VEC_free (constructor_elt, gc, n); + return t; + } + t = build_constructor (TREE_TYPE (t), n); + TREE_CONSTANT (t) = true; + return t; +} + +/* Subroutine of cxx_eval_constant_expression. + The expression tree T is a VEC_INIT_EXPR which denotes the desired + initialization of a non-static data member of array type. Reduce it to a + CONSTRUCTOR. + + Note that this is only intended to support the initializations done by + defaulted constructors for classes with non-static data members of array + type. In this case, VEC_INIT_EXPR_INIT will either be NULL_TREE for the + default constructor, or a COMPONENT_REF for the copy/move + constructor. */ + +static tree +cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree elttype = TREE_TYPE (atype); + int max = tree_low_cst (array_type_nelts (atype), 0); + VEC(constructor_elt,gc) *n = VEC_alloc (constructor_elt, gc, max + 1); + int i; + + /* For the default constructor, build up a call to the default + constructor of the element type. We only need to handle class types + here, as for a constructor to be constexpr, all members must be + initialized, which for a defaulted default constructor means they must + be of a class type with a constexpr default constructor. */ + if (!init) + { + VEC(tree,gc) *argvec = make_tree_vector (); + init = build_special_member_call (NULL_TREE, complete_ctor_identifier, + &argvec, elttype, LOOKUP_NORMAL, + tf_warning_or_error); + release_tree_vector (argvec); + init = cxx_eval_constant_expression (call, init, allow_non_constant, + addr, non_constant_p); + } + + if (*non_constant_p && !allow_non_constant) + goto fail; + + for (i = 0; i <= max; ++i) + { + tree idx = build_int_cst (size_type_node, i); + tree eltinit; + if (TREE_CODE (elttype) == ARRAY_TYPE) + { + /* A multidimensional array; recurse. */ + eltinit = cp_build_array_ref (input_location, init, idx, + tf_warning_or_error); + eltinit = cxx_eval_vec_init_1 (call, elttype, eltinit, + allow_non_constant, addr, + non_constant_p); + } + else if (TREE_CODE (init) == CONSTRUCTOR) + { + /* Initializing an element using the call to the default + constructor we just built above. */ + eltinit = unshare_expr (init); + } + else + { + /* Copying an element. */ + VEC(tree,gc) *argvec; + gcc_assert (same_type_ignoring_top_level_qualifiers_p + (atype, TREE_TYPE (init))); + eltinit = cp_build_array_ref (input_location, init, idx, + tf_warning_or_error); + if (!real_lvalue_p (init)) + eltinit = move (eltinit); + argvec = make_tree_vector (); + VEC_quick_push (tree, argvec, eltinit); + eltinit = (build_special_member_call + (NULL_TREE, complete_ctor_identifier, &argvec, + elttype, LOOKUP_NORMAL, tf_warning_or_error)); + release_tree_vector (argvec); + eltinit = cxx_eval_constant_expression + (call, eltinit, allow_non_constant, addr, non_constant_p); + } + if (*non_constant_p && !allow_non_constant) + goto fail; + CONSTRUCTOR_APPEND_ELT (n, idx, eltinit); + } + + if (!*non_constant_p) + { + init = build_constructor (TREE_TYPE (atype), n); + TREE_CONSTANT (init) = true; + return init; + } + + fail: + VEC_free (constructor_elt, gc, n); + return init; +} + +static tree +cxx_eval_vec_init (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree atype = TREE_TYPE (t); + tree init = VEC_INIT_EXPR_INIT (t); + tree r = cxx_eval_vec_init_1 (call, atype, init, allow_non_constant, + addr, non_constant_p); + if (*non_constant_p) + return t; + else + return r; +} + +/* A less strict version of fold_indirect_ref_1, which requires cv-quals to + match. We want to be less strict for simple *& folding; if we have a + non-const temporary that we access through a const pointer, that should + work. We handle this here rather than change fold_indirect_ref_1 + because we're dealing with things like ADDR_EXPR of INTEGER_CST which + don't really make sense outside of constant expression evaluation. Also + we want to allow folding to COMPONENT_REF, which could cause trouble + with TBAA in fold_indirect_ref_1. */ + +static tree +cxx_eval_indirect_ref (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree orig_op0 = TREE_OPERAND (t, 0); + tree op0 = cxx_eval_constant_expression (call, orig_op0, allow_non_constant, + /*addr*/false, non_constant_p); + tree type, sub, subtype, r; + bool empty_base; + + /* Don't VERIFY_CONSTANT here. */ + if (*non_constant_p) + return t; + + type = TREE_TYPE (t); + sub = op0; + r = NULL_TREE; + empty_base = false; + + STRIP_NOPS (sub); + subtype = TREE_TYPE (sub); + gcc_assert (POINTER_TYPE_P (subtype)); + + if (TREE_CODE (sub) == ADDR_EXPR) + { + tree op = TREE_OPERAND (sub, 0); + tree optype = TREE_TYPE (op); + + if (same_type_ignoring_top_level_qualifiers_p (optype, type)) + r = op; + /* Also handle conversion to an empty base class, which + is represented with a NOP_EXPR. */ + else if (!addr && is_empty_class (type) + && CLASS_TYPE_P (optype) + && DERIVED_FROM_P (type, optype)) + { + r = op; + empty_base = true; + } + /* *(foo *)&struct_with_foo_field => COMPONENT_REF */ + else if (RECORD_OR_UNION_TYPE_P (optype)) + { + tree field = TYPE_FIELDS (optype); + for (; field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && integer_zerop (byte_position (field)) + && (same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (field), type))) + { + r = fold_build3 (COMPONENT_REF, type, op, field, NULL_TREE); + break; + } + } + } + else if (TREE_CODE (sub) == POINTER_PLUS_EXPR + && TREE_CODE (TREE_OPERAND (sub, 1)) == INTEGER_CST) + { + tree op00 = TREE_OPERAND (sub, 0); + tree op01 = TREE_OPERAND (sub, 1); + + STRIP_NOPS (op00); + if (TREE_CODE (op00) == ADDR_EXPR) + { + tree op00type; + op00 = TREE_OPERAND (op00, 0); + op00type = TREE_TYPE (op00); + + /* ((foo *)&struct_with_foo_field)[1] => COMPONENT_REF */ + if (RECORD_OR_UNION_TYPE_P (op00type)) + { + tree field = TYPE_FIELDS (op00type); + for (; field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && tree_int_cst_equal (byte_position (field), op01) + && (same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (field), type))) + { + r = fold_build3 (COMPONENT_REF, type, op00, + field, NULL_TREE); + break; + } + } + } + } + + /* Let build_fold_indirect_ref handle the cases it does fine with. */ + if (r == NULL_TREE) + r = build_fold_indirect_ref (op0); + if (TREE_CODE (r) != INDIRECT_REF) + r = cxx_eval_constant_expression (call, r, allow_non_constant, + addr, non_constant_p); + else if (TREE_CODE (sub) == ADDR_EXPR + || TREE_CODE (sub) == POINTER_PLUS_EXPR) + { + gcc_assert (!same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (TREE_TYPE (sub)), TREE_TYPE (t))); + /* FIXME Mike Miller wants this to be OK. */ + if (!allow_non_constant) + error ("accessing value of %qE through a %qT glvalue in a " + "constant expression", build_fold_indirect_ref (sub), + TREE_TYPE (t)); + *non_constant_p = true; + return t; + } + + /* If we're pulling out the value of an empty base, make sure + that the whole object is constant and then return an empty + CONSTRUCTOR. */ + if (empty_base) + { + VERIFY_CONSTANT (r); + r = build_constructor (TREE_TYPE (t), NULL); + TREE_CONSTANT (r) = true; + } + + if (TREE_CODE (r) == INDIRECT_REF && TREE_OPERAND (r, 0) == orig_op0) + return t; + return r; +} + +/* Attempt to reduce the expression T to a constant value. + On failure, issue diagnostic and return error_mark_node. */ +/* FIXME unify with c_fully_fold */ + +static tree +cxx_eval_constant_expression (const constexpr_call *call, tree t, + bool allow_non_constant, bool addr, + bool *non_constant_p) +{ + tree r = t; + + if (t == error_mark_node) + { + *non_constant_p = true; + return t; + } + if (CONSTANT_CLASS_P (t)) + { + if (TREE_CODE (t) == PTRMEM_CST) + t = cplus_expand_constant (t); + return t; + } + if (TREE_CODE (t) != NOP_EXPR + && reduced_constant_expression_p (t)) + return fold (t); + + switch (TREE_CODE (t)) + { + case VAR_DECL: + if (addr) + return t; + /* else fall through. */ + case CONST_DECL: + r = integral_constant_value (t); + if (TREE_CODE (r) == TARGET_EXPR + && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR) + r = TARGET_EXPR_INITIAL (r); + if (DECL_P (r)) + { + if (!allow_non_constant) + error ("%qD cannot appear in a constant expression", r); + *non_constant_p = true; + } + break; + + case FUNCTION_DECL: + case LABEL_DECL: + return t; + + case PARM_DECL: + if (call && DECL_CONTEXT (t) == call->fundef->decl) + r = lookup_parameter_binding (call, t); + else if (addr) + /* Defer in case this is only used for its type. */; + else + { + if (!allow_non_constant) + error ("%qE is not a constant expression", t); + *non_constant_p = true; + } + break; + + case CALL_EXPR: + case AGGR_INIT_EXPR: + r = cxx_eval_call_expression (call, t, allow_non_constant, addr, + non_constant_p); + break; + + case TARGET_EXPR: + case INIT_EXPR: + /* Pass false for 'addr' because these codes indicate + initialization of a temporary. */ + r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), + allow_non_constant, false, + non_constant_p); + if (!*non_constant_p) + /* Adjust the type of the result to the type of the temporary. */ + r = adjust_temp_type (TREE_TYPE (t), r); + break; + + case SCOPE_REF: + r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), + allow_non_constant, addr, + non_constant_p); + break; + + case RETURN_EXPR: + case NON_LVALUE_EXPR: + case TRY_CATCH_EXPR: + case CLEANUP_POINT_EXPR: + case MUST_NOT_THROW_EXPR: + case SAVE_EXPR: + r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), + allow_non_constant, addr, + non_constant_p); + break; + + /* These differ from cxx_eval_unary_expression in that this doesn't + check for a constant operand or result; an address can be + constant without its operand being, and vice versa. */ + case INDIRECT_REF: + r = cxx_eval_indirect_ref (call, t, allow_non_constant, addr, + non_constant_p); + break; + + case ADDR_EXPR: + { + tree oldop = TREE_OPERAND (t, 0); + tree op = cxx_eval_constant_expression (call, oldop, + allow_non_constant, + /*addr*/true, + non_constant_p); + /* Don't VERIFY_CONSTANT here. */ + if (*non_constant_p) + return t; + /* This function does more aggressive folding than fold itself. */ + r = build_fold_addr_expr_with_type (op, TREE_TYPE (t)); + if (TREE_CODE (r) == ADDR_EXPR && TREE_OPERAND (r, 0) == oldop) + return t; + break; + } + + case REALPART_EXPR: + case IMAGPART_EXPR: + case CONJ_EXPR: + case FIX_TRUNC_EXPR: + case FLOAT_EXPR: + case NEGATE_EXPR: + case ABS_EXPR: + case BIT_NOT_EXPR: + case TRUTH_NOT_EXPR: + case FIXED_CONVERT_EXPR: + r = cxx_eval_unary_expression (call, t, allow_non_constant, addr, + non_constant_p); + break; + + case COMPOUND_EXPR: + { + /* check_return_expr sometimes wraps a TARGET_EXPR in a + COMPOUND_EXPR; don't get confused. Also handle EMPTY_CLASS_EXPR + introduced by build_call_a. */ + tree op0 = TREE_OPERAND (t, 0); + tree op1 = TREE_OPERAND (t, 1); + STRIP_NOPS (op1); + if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0)) + || TREE_CODE (op1) == EMPTY_CLASS_EXPR) + r = cxx_eval_constant_expression (call, op0, allow_non_constant, + addr, non_constant_p); + else + goto binary; + } + break; + + case POINTER_PLUS_EXPR: + case PLUS_EXPR: + case MINUS_EXPR: + case MULT_EXPR: + case TRUNC_DIV_EXPR: + case CEIL_DIV_EXPR: + case FLOOR_DIV_EXPR: + case ROUND_DIV_EXPR: + case TRUNC_MOD_EXPR: + case CEIL_MOD_EXPR: + case ROUND_MOD_EXPR: + case RDIV_EXPR: + case EXACT_DIV_EXPR: + case MIN_EXPR: + case MAX_EXPR: + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case BIT_AND_EXPR: + case TRUTH_XOR_EXPR: + case LT_EXPR: + case LE_EXPR: + case GT_EXPR: + case GE_EXPR: + case EQ_EXPR: + case NE_EXPR: + case UNORDERED_EXPR: + case ORDERED_EXPR: + case UNLT_EXPR: + case UNLE_EXPR: + case UNGT_EXPR: + case UNGE_EXPR: + case UNEQ_EXPR: + case RANGE_EXPR: + case COMPLEX_EXPR: + binary: + r = cxx_eval_binary_expression (call, t, allow_non_constant, addr, + non_constant_p); + break; + + /* fold can introduce non-IF versions of these; still treat them as + short-circuiting. */ + case TRUTH_AND_EXPR: + case TRUTH_ANDIF_EXPR: + r = cxx_eval_logical_expression (call, t, boolean_false_node, + boolean_true_node, + allow_non_constant, addr, + non_constant_p); + break; + + case TRUTH_OR_EXPR: + case TRUTH_ORIF_EXPR: + r = cxx_eval_logical_expression (call, t, boolean_true_node, + boolean_false_node, + allow_non_constant, addr, + non_constant_p); + break; + + case ARRAY_REF: + r = cxx_eval_array_reference (call, t, allow_non_constant, addr, + non_constant_p); + break; + + case COMPONENT_REF: + r = cxx_eval_component_reference (call, t, allow_non_constant, addr, + non_constant_p); + break; + + case COND_EXPR: + case VEC_COND_EXPR: + r = cxx_eval_conditional_expression (call, t, allow_non_constant, addr, + non_constant_p); + break; + + case CONSTRUCTOR: + r = cxx_eval_bare_aggregate (call, t, allow_non_constant, addr, + non_constant_p); + break; + + case VEC_INIT_EXPR: + /* We can get this in a defaulted constructor for a class with a + non-static data member of array type. Either the initializer will + be NULL, meaning default-initialization, or it will be an lvalue + or xvalue of the same type, meaning direct-initialization from the + corresponding member. */ + r = cxx_eval_vec_init (call, t, allow_non_constant, addr, + non_constant_p); + break; + + case CONVERT_EXPR: + case VIEW_CONVERT_EXPR: + case NOP_EXPR: + { + tree oldop = TREE_OPERAND (t, 0); + tree op = oldop; + tree to = TREE_TYPE (t); + tree source = TREE_TYPE (op); + if (TYPE_PTR_P (source) && ARITHMETIC_TYPE_P (to) + && !(TREE_CODE (op) == COMPONENT_REF + && TYPE_PTRMEMFUNC_P (TREE_TYPE (TREE_OPERAND (op, 0))))) + { + if (!allow_non_constant) + error ("conversion of expression %qE of pointer type " + "cannot yield a constant expression", op); + *non_constant_p = true; + return t; + } + op = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), + allow_non_constant, addr, + non_constant_p); + if (*non_constant_p) + return t; + if (op == oldop) + /* We didn't fold at the top so we could check for ptr-int + conversion. */ + return fold (t); + r = fold_build1 (TREE_CODE (t), to, op); + } + break; + + case EMPTY_CLASS_EXPR: + /* This is good enough for a function argument that might not get + used, and they can't do anything with it, so just return it. */ + return t; + + case LAMBDA_EXPR: + case DYNAMIC_CAST_EXPR: + case PSEUDO_DTOR_EXPR: + case PREINCREMENT_EXPR: + case POSTINCREMENT_EXPR: + case PREDECREMENT_EXPR: + case POSTDECREMENT_EXPR: + case NEW_EXPR: + case VEC_NEW_EXPR: + case DELETE_EXPR: + case VEC_DELETE_EXPR: + case THROW_EXPR: + case MODIFY_EXPR: + case MODOP_EXPR: + /* GCC internal stuff. */ + case VA_ARG_EXPR: + case OBJ_TYPE_REF: + case WITH_CLEANUP_EXPR: + case STATEMENT_LIST: + case BIND_EXPR: + case NON_DEPENDENT_EXPR: + case BASELINK: + case EXPR_STMT: + if (!allow_non_constant) + error_at (EXPR_LOC_OR_HERE (t), + "expression %qE is not a constant-expression", t); + *non_constant_p = true; + break; + + default: + internal_error ("unexpected expression %qE of kind %s", t, + tree_code_name[TREE_CODE (t)]); + *non_constant_p = true; + break; + } + + if (r == error_mark_node) + *non_constant_p = true; + + if (*non_constant_p) + return t; + else + return r; +} + +static tree +cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant) +{ + bool non_constant_p = false; + tree r = cxx_eval_constant_expression (NULL, t, allow_non_constant, + false, &non_constant_p); + + if (!non_constant_p && !reduced_constant_expression_p (r)) + { + if (!allow_non_constant) + error ("%qE is not a constant expression", t); + non_constant_p = true; + } + + if (non_constant_p && !allow_non_constant) + return error_mark_node; + else if (non_constant_p && TREE_CONSTANT (t)) + { + /* This isn't actually constant, so unset TREE_CONSTANT. */ + if (EXPR_P (t) || TREE_CODE (t) == CONSTRUCTOR) + r = copy_node (t); + else + r = build_nop (TREE_TYPE (t), t); + TREE_CONSTANT (r) = false; + return r; + } + else if (non_constant_p || r == t) + return t; + else if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r))) + { + if (TREE_CODE (t) == TARGET_EXPR + && TARGET_EXPR_INITIAL (t) == r) + return t; + else + { + r = get_target_expr (r); + TREE_CONSTANT (r) = true; + return r; + } + } + else + return r; +} + +/* If T represents a constant expression returns its reduced value. + Otherwise return error_mark_node. If T is dependent, then + return NULL. */ + +tree +cxx_constant_value (tree t) +{ + return cxx_eval_outermost_constant_expr (t, false); +} + +/* If T is a constant expression, returns its reduced value. + Otherwise, if T does not have TREE_CONSTANT set, returns T. + Otherwise, returns a version of T without TREE_CONSTANT. */ + +tree +maybe_constant_value (tree t) +{ + tree r; + + if (type_dependent_expression_p (t) + /* FIXME shouldn't check value-dependence first; see comment before + value_dependent_expression_p. */ + || value_dependent_expression_p (t)) + return t; + + r = cxx_eval_outermost_constant_expr (t, true); +#ifdef ENABLE_CHECKING + /* cp_tree_equal looks through NOPs, so allow them. */ + gcc_assert (r == t + || CONVERT_EXPR_P (t) + || (TREE_CONSTANT (t) && !TREE_CONSTANT (r)) + || !cp_tree_equal (r, t)); +#endif + return r; +} + +/* Like maybe_constant_value, but returns a CONSTRUCTOR directly, rather + than wrapped in a TARGET_EXPR. */ + +tree +maybe_constant_init (tree t) +{ + t = maybe_constant_value (t); + if (TREE_CODE (t) == TARGET_EXPR) + { + tree init = TARGET_EXPR_INITIAL (t); + if (TREE_CODE (init) == CONSTRUCTOR + && TREE_CONSTANT (init)) + t = init; + } + return t; +} + /* Return true if the object referred to by REF has automatic or thread local storage. */ commit 0b11bb56b4b96bd71492bc5f7fd7c4f91687634f Author: Jason Merrill Date: Wed Oct 27 10:26:38 2010 -0400 * 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. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 204fda5..a1c8682 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -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. */ diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 1c9fac1..217450c 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -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) { diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 96fefae..e408ef7 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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 { diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 95d0ab9..d2d6f4a 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -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; diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index de6f0c4..fb5ca7f 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -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 %", 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) { diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 3f492bb..0003601 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -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)) { diff --git a/gcc/cp/except.c b/gcc/cp/except.c index cf8a210..f02096e 100644 --- a/gcc/cp/except.c +++ b/gcc/cp/except.c @@ -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) diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 3a6e2e7..d632816 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -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; } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 33d8561..9027760 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -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); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 9a72ea8..d3c1c1c 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -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. */ diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c index da56d4d..709e44c 100644 --- a/gcc/cp/rtti.c +++ b/gcc/cp/rtti.c @@ -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)) diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 039f0a5..397d383 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -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; diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 511730a..e55b5bc 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -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); } diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 967fe4d..0da3278 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -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; diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index b9c027a..f1bd16e 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -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. */ diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr.C new file mode 100644 index 0000000..36939e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr.C @@ -0,0 +1,15 @@ +// { dg-options -std=c++0x } + +template +constexpr T do_get(T* x, int n) { + return x[n - 1]; +} + +template +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"); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr2.C new file mode 100644 index 0000000..7cf7334 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr2.C @@ -0,0 +1,21 @@ +// { dg-options -std=c++0x } + +template +struct IsNegative { + int dummy; // Workaround for empty class problem + constexpr IsNegative() : dummy(0) {} + constexpr bool operator()(const T& x) { + return x < T(0); + } +}; + +template +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{}); // #1 + +static_assert(answer, "Error"); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr3.C new file mode 100644 index 0000000..f84cb52 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr3.C @@ -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"); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr4.C new file mode 100644 index 0000000..697d2d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr4.C @@ -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 +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"); + diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr5.C new file mode 100644 index 0000000..d58f254 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr5.C @@ -0,0 +1,32 @@ +// { dg-options -std=c++0x } + +template +constexpr T do_last(T* x, int n) { + return x[n - 1]; // +} + +template +constexpr T last(T (&x)[N]) { + return do_last(x, N); +} + +constexpr bool is_negative(int x) { return x < 0; } + +template +struct IsNegative { + constexpr bool operator()(const T& x) { + return x < T(0); + } +}; + +template +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{}); // Line 27 +constexpr auto answer2 = has_neg(a, is_negative); + +static_assert(answer2 == answer1, "Error"); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr6.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr6.C new file mode 100644 index 0000000..17dd6e5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr6.C @@ -0,0 +1,18 @@ +// { dg-options "-std=c++0x" } + +typedef decltype(sizeof(char)) size_type; + +template +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 (¶m)[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"); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array.C new file mode 100644 index 0000000..e37400a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array.C @@ -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{}; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-base.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-base.C new file mode 100644 index 0000000..774df31 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-base.C @@ -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); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-complex.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-complex.C new file mode 100644 index 0000000..fbaae5d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-complex.C @@ -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; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-data1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-data1.C new file mode 100644 index 0000000..7d7a33d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-data1.C @@ -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; +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-data2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-data2.C new file mode 100644 index 0000000..4d4ce78 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-data2.C @@ -0,0 +1,48 @@ +// { dg-do "compile" } +// { dg-options "-std=gnu++0x" } + +template + struct A3 + { + typedef _Tp value_type; + typedef A3 type; + + static constexpr value_type value = v; + + constexpr operator value_type() { return value; } + }; + +// Partial specialization. +template + struct A3<_Tp*, v> + { + typedef _Tp* value_type; + typedef A3 type; + + static constexpr value_type value = v; + + constexpr operator value_type() { return value; } + }; + +// Explicit specialization. +template<> + struct A3 + { + typedef unsigned short value_type; + typedef A3 type; + + static constexpr value_type value = 0; + + constexpr operator value_type() { return value; } + }; + +// Explicitly instantiate. +template struct A3; + +// Extern explicitly instantiate. +extern template struct A3; + +// Use. +A3 a31; +// FIXME should this be an error? +A3 a32; // { dg-warning "overflow" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-deref.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-deref.C new file mode 100644 index 0000000..7363e98 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-deref.C @@ -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 +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag1.C new file mode 100644 index 0000000..a3706d6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag1.C @@ -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 +struct A +{ + T t; + constexpr int f() { return 42; } +}; + +struct B { B(); operator int(); }; + +constexpr A ai = { 42 }; +constexpr int i = ai.f(); + +constexpr int b = A().f(); // { dg-error "enclosing class" } + +template +constexpr int f (T t) { return 42; } +constexpr int x = f(B()); // { dg-error "parameter" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-eh-spec.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-eh-spec.C new file mode 100644 index 0000000..6d231fa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-eh-spec.C @@ -0,0 +1,9 @@ +// { dg-options -std=c++0x } +template class my_limits { +public: + static constexpr T min() throw() { return T(); } + static constexpr T max() noexcept { return T(); } +}; + +constexpr double var_min = my_limits::min(); // #1 OK +constexpr double var_max = my_limits::max(); // #2 Error diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis.C new file mode 100644 index 0000000..5d090b5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis.C @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis2.C new file mode 100644 index 0000000..0bb6904 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis2.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-empty.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty.C new file mode 100644 index 0000000..a9fc438 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty.C @@ -0,0 +1,7 @@ +// { dg-options -std=c++0x } + +struct Empty {}; + +constexpr bool f(Empty) { return true; } + +constexpr bool x(f(Empty{})); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-empty2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty2.C new file mode 100644 index 0000000..ef21211 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty2.C @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-empty3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty3.C new file mode 100644 index 0000000..e0026fc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty3.C @@ -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); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-empty4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty4.C new file mode 100644 index 0000000..b07f924 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty4.C @@ -0,0 +1,34 @@ +// { dg-options -std=c++0x } + +typedef decltype(sizeof(char)) size_type; + +template +constexpr size_type do_find_if_or_stop(T (&x)[N], size_type i, Pred p); + +template +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 +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 +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 +struct IsNegative { + constexpr bool operator()(const T& x) { + return x < T(0); + } +}; + +constexpr auto pos1 = find_if(items_long, IsNegative{}); // Line 30 + +static_assert(pos1 == 4, "find_if failure"); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-empty5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty5.C new file mode 100644 index 0000000..9bd9aa5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-empty5.C @@ -0,0 +1,7 @@ +// { dg-options -std=c++0x } + +struct A { }; +struct B: A { }; + +constexpr B b { }; +constexpr A a = b; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C new file mode 100644 index 0000000..f2b5384 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex2.C new file mode 100644 index 0000000..5896c20 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex2.C @@ -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 struct X { static const int i = I; }; +constexpr A a = 42; + +X x; // OK: unique conversion to int +int ar[X::i]; // also OK +int ary[a]; // { dg-error "ambiguous|conversion|array" } ambiguous conversion + diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C new file mode 100644 index 0000000..597603c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C @@ -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 +struct B +{ + T t; + constexpr B(T _t): t(_t) { } +}; + +B b(1); +SA(b.t==1); // { dg-error "non-constant condition|'b'" } +constexpr B b2(1); +SA(b2.t==1); + +template +constexpr T f(T a, T b) +{ + typedef T myT; + return a + b; +} + +SA(f(1,2)==3); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex4.C new file mode 100644 index 0000000..4214f5c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex4.C @@ -0,0 +1,16 @@ +// { dg-options "-std=c++0x" } + +struct A +{ + constexpr A(int) { } + constexpr operator int() { return 1; }; +}; + +template +struct B +{ + static constexpr A a = A(1); + int ar[a]; +}; + +B b; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-explicit-inst.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-explicit-inst.C new file mode 100644 index 0000000..8f0da0a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-explicit-inst.C @@ -0,0 +1,9 @@ +// { dg-options -std=c++0x } + +template 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 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-fnptr.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-fnptr.C new file mode 100644 index 0000000..4c84d82 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-fnptr.C @@ -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"); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-function1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-function1.C new file mode 100644 index 0000000..afe964b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-function1.C @@ -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; } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C new file mode 100644 index 0000000..5e0c101 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C @@ -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() isn’t (yet) defined + +constexpr int fac(int x) +{ return x > 2 ? x * fac(x - 1) : 1; } // OK diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-function3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-function3.C new file mode 100644 index 0000000..07981fb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-function3.C @@ -0,0 +1,30 @@ +// { dg-do "compile" } +// { dg-options "-std=gnu++0x" } + +// From N2235 + +// function template 1 +template + constexpr int bytesize(T t) + { return sizeof (t); } // OK + +char buf[bytesize(0)]; // OK -- not C99 VLA + + +// function template 2 +template + 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); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice.C new file mode 100644 index 0000000..3b72484 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-initlist.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-initlist.C new file mode 100644 index 0000000..6854e73 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-initlist.C @@ -0,0 +1,65 @@ +// { dg-options -std=c++0x } +// { dg-do run } + +namespace xstd { + +typedef decltype(sizeof(char)) size_t; + +template +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 + 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 +constexpr initializer_list make_list(const E(&array)[N]) { + return initializer_list(array); +} + +template +E min(initializer_list 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 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; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C new file mode 100644 index 0000000..ce01f8b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C @@ -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 "" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept.C new file mode 100644 index 0000000..0476f90 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept.C @@ -0,0 +1,13 @@ +// { dg-options -std=c++0x } + +template +struct is_funny { + static constexpr bool value = false; +}; + +template +constexpr T value(T t) noexcept(is_funny::value) { return t; } // Line 7 + +constexpr bool ok = noexcept(value(42)); + +static_assert(ok, "Assertion failure"); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept2.C new file mode 100644 index 0000000..95a1443 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept2.C @@ -0,0 +1,14 @@ +// { dg-options -std=c++0x } + +template +constexpr T value(T t) { return t; } + +template +struct is_funny { + static constexpr bool value = false; +}; + +template +void eval() noexcept(value(is_funny::value)) {} + +constexpr bool ok = noexcept(eval()); // line 12 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept3.C new file mode 100644 index 0000000..6e76ea8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept3.C @@ -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))); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept4.C new file mode 100644 index 0000000..119d4e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept4.C @@ -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))); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C new file mode 100644 index 0000000..2c53595 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-nonlit.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nonlit.C new file mode 100644 index 0000000..9104c8a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nonlit.C @@ -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 +constexpr bool ignore(T&&) { return true; } + +static_assert(ignore(10), "Error"); // OK + +A s; + +static_assert(ignore(s), "Error"); // Currently an error diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-nonlit2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nonlit2.C new file mode 100644 index 0000000..21e8bd5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nonlit2.C @@ -0,0 +1,19 @@ +// { dg-options -std=c++0x } + +struct A +{ + ~A(); +}; + +template +struct W { + T t; + template + constexpr W(U&& u) : t(u) {} +}; + +template +constexpr W make_w(T& w) { return W(w); } + +A a; +constexpr auto w = make_w(a); // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C new file mode 100644 index 0000000..7ac53db --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C @@ -0,0 +1,6 @@ +// { dg-options -std=c++0x } + +constexpr int zero() { return 0; } + +void* ptr1 = zero(); // #1 +constexpr void* ptr2 = zero(); // #2 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-object1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-object1.C new file mode 100644 index 0000000..c635fd0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-object1.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-object2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-object2.C new file mode 100644 index 0000000..3590bb8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-object2.C @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-overflow.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-overflow.C new file mode 100644 index 0000000..9b3b1fa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-overflow.C @@ -0,0 +1,6 @@ +// { dg-options "-std=c++0x -w" } + +#include +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 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-pedantic.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-pedantic.C new file mode 100644 index 0000000..dc393d7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-pedantic.C @@ -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 +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); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-pos1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-pos1.C new file mode 100644 index 0000000..775080a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-pos1.C @@ -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); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-potential1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-potential1.C new file mode 100644 index 0000000..e933506 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-potential1.C @@ -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; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ptrmem.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ptrmem.C new file mode 100644 index 0000000..f6ed2f4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ptrmem.C @@ -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"); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-pure.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-pure.C new file mode 100644 index 0000000..e17e02a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-pure.C @@ -0,0 +1,13 @@ +// { dg-options -std=c++0x } + +struct A +{ + virtual void f() = 0; +}; + +struct B: A +{ + void f() { } +}; + +B b; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-static.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-static.C new file mode 100644 index 0000000..8ed2b5e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static.C @@ -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; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-static2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-static2.C new file mode 100644 index 0000000..67c3530 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static2.C @@ -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 +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-static3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-static3.C new file mode 100644 index 0000000..dccdc85 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static3.C @@ -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; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-static4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-static4.C new file mode 100644 index 0000000..8189fc5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static4.C @@ -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() +{ +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-static5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-static5.C new file mode 100644 index 0000000..cb553a2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static5.C @@ -0,0 +1,17 @@ +// { dg-options -std=c++0x } + +template +struct A +{ + constexpr static T t; +}; +template +constexpr T A::t = T(); // { dg-error "not literal" } + +struct B +{ + ~B(); +}; + +B b = A::t; + diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-static6.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-static6.C new file mode 100644 index 0000000..a34704d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static6.C @@ -0,0 +1,21 @@ +// { dg-options -std=c++0x } + +struct B +{ + constexpr operator int() { return 4; } +}; + +template +struct C; + +template<> +struct C<4> { typedef int TP; }; + +template +struct A +{ + constexpr static B t = B(); + C::TP tp; +}; + +A a; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C new file mode 100644 index 0000000..e76d00d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-switch.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-switch.C new file mode 100644 index 0000000..d229304 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-switch.C @@ -0,0 +1,13 @@ +// { dg-options -std=c++0x } + +template +constexpr T value(T t = T()) { return t; } + +enum us_enum { us_item = value() }; // OK + +void func(us_enum n) { + switch (n) { + case value(us_item): ; // #1 Error + default: ; + } +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C new file mode 100644 index 0000000..0c8c73d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-targ.C @@ -0,0 +1,13 @@ +// { dg-options -std=c++0x } + +struct A +{ + constexpr operator double() { return 1.0; } +}; + +template +struct B +{ }; + +constexpr A a { }; +B b; // { dg-error "template argument|invalid type" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C new file mode 100644 index 0000000..345b240 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C @@ -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); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-typeid.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-typeid.C new file mode 100644 index 0000000..b523bb3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-typeid.C @@ -0,0 +1,10 @@ +// { dg-options -std=c++0x } + +#include + +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 *-*-* } } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-union.C new file mode 100644 index 0000000..b461305 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-value.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-value.C new file mode 100644 index 0000000..85799d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-value.C @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-value2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-value2.C new file mode 100644 index 0000000..1b0e28f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-value2.C @@ -0,0 +1,19 @@ +// { dg-options -std=c++0x } + +template +constexpr T value_init() { return T(); } + +template +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(); +constexpr auto u2 = value_init(); +constexpr auto u3 = value_init(); +constexpr auto u4 = bar(EmptyUnion{}); +constexpr auto u5 = bar(Union1{}); +constexpr auto u6 = bar(Union3{}); +constexpr auto u7 = bar(u1); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-variadic.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-variadic.C new file mode 100644 index 0000000..5d0ad05 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-variadic.C @@ -0,0 +1,9 @@ +// { dg-options -std=c++0x } +template +constexpr bool variadics(T&&...) { return true; } + +struct IsLiteral {}; + +constexpr bool variadic_var = variadics(0, true, 1.2, IsLiteral{}); // Error, so below + +int main() {} diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist5.C b/gcc/testsuite/g++.dg/cpp0x/initlist5.C index 958007c..32caac3 100644 --- a/gcc/testsuite/g++.dg/cpp0x/initlist5.C +++ b/gcc/testsuite/g++.dg/cpp0x/initlist5.C @@ -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 }; diff --git a/gcc/testsuite/g++.dg/cpp0x/static_assert3.C b/gcc/testsuite/g++.dg/cpp0x/static_assert3.C index 69f93ab..1ff2ffc 100644 --- a/gcc/testsuite/g++.dg/cpp0x/static_assert3.C +++ b/gcc/testsuite/g++.dg/cpp0x/static_assert3.C @@ -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 } diff --git a/gcc/testsuite/g++.dg/other/fold1.C b/gcc/testsuite/g++.dg/other/fold1.C index b075fc1..23d3454 100644 --- a/gcc/testsuite/g++.dg/other/fold1.C +++ b/gcc/testsuite/g++.dg/other/fold1.C @@ -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" } }; diff --git a/gcc/testsuite/g++.dg/parse/constant4.C b/gcc/testsuite/g++.dg/parse/constant4.C index d69f60b..b2c112c 100644 --- a/gcc/testsuite/g++.dg/parse/constant4.C +++ b/gcc/testsuite/g++.dg/parse/constant4.C @@ -18,7 +18,7 @@ void Foo () static const unsigned J = X::J; - Y j; // { dg-error "non-constant" "" } + Y j; // { dg-error "constant" "" } } struct A diff --git a/gcc/testsuite/g++.dg/parse/crash36.C b/gcc/testsuite/g++.dg/parse/crash36.C index 6116eb0..a5a142c 100644 --- a/gcc/testsuite/g++.dg/parse/crash36.C +++ b/gcc/testsuite/g++.dg/parse/crash36.C @@ -9,4 +9,4 @@ template struct A // { dg-warning "variadic templates" } static const int i = sizeof (++t); // { dg-error "was not declared in this scope" } }; -int x[A ::i]; +int x[A ::i]; // { dg-error "constant-expression" } diff --git a/gcc/testsuite/g++.dg/template/arg5.C b/gcc/testsuite/g++.dg/template/arg5.C index 3d67143..87cbd02 100644 --- a/gcc/testsuite/g++.dg/template/arg5.C +++ b/gcc/testsuite/g++.dg/template/arg5.C @@ -5,5 +5,5 @@ template struct A; template void foo() { - A<__builtin_constant_p(.)> a; // { dg-error "template argument" } + A<__builtin_constant_p(.)> a; // { dg-error "template argument|invalid" } } diff --git a/gcc/testsuite/g++.dg/template/non-dependent10.C b/gcc/testsuite/g++.dg/template/non-dependent10.C index 0adac25..1891cad 100644 --- a/gcc/testsuite/g++.dg/template/non-dependent10.C +++ b/gcc/testsuite/g++.dg/template/non-dependent10.C @@ -18,5 +18,5 @@ struct X template struct Foo { - X<&S::f> x; // { dg-error "convert|no type" } + X<&S::f> x; // { dg-error "convert|no matches" } }; diff --git a/gcc/testsuite/g++.dg/template/qualified-id3.C b/gcc/testsuite/g++.dg/template/qualified-id3.C index d97ef5c..1fc1cc3 100644 --- a/gcc/testsuite/g++.dg/template/qualified-id3.C +++ b/gcc/testsuite/g++.dg/template/qualified-id3.C @@ -3,7 +3,7 @@ template struct A { }; template struct B { static const int c; - typedef A::c> C; // { dg-error "non-constant" } + typedef A::c> C; // { dg-error "constant expression" } }; template const int B::c = sizeof (T); diff --git a/gcc/testsuite/g++.dg/warn/overflow-warn-1.C b/gcc/testsuite/g++.dg/warn/overflow-warn-1.C index 78f4348..17bd067 100644 --- a/gcc/testsuite/g++.dg/warn/overflow-warn-1.C +++ b/gcc/testsuite/g++.dg/warn/overflow-warn-1.C @@ -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" } */ diff --git a/gcc/testsuite/g++.dg/warn/overflow-warn-3.C b/gcc/testsuite/g++.dg/warn/overflow-warn-3.C index fa45eeb..ce03a97 100644 --- a/gcc/testsuite/g++.dg/warn/overflow-warn-3.C +++ b/gcc/testsuite/g++.dg/warn/overflow-warn-3.C @@ -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" } */ diff --git a/gcc/testsuite/g++.dg/warn/overflow-warn-4.C b/gcc/testsuite/g++.dg/warn/overflow-warn-4.C index ae06a80..0c916d0 100644 --- a/gcc/testsuite/g++.dg/warn/overflow-warn-4.C +++ b/gcc/testsuite/g++.dg/warn/overflow-warn-4.C @@ -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" } */ diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash10.C b/gcc/testsuite/g++.old-deja/g++.pt/crash10.C index a66450f..af0e919 100644 --- a/gcc/testsuite/g++.old-deja/g++.pt/crash10.C +++ b/gcc/testsuite/g++.old-deja/g++.pt/crash10.C @@ -4,7 +4,7 @@ template class GCD { public: enum { val = (N == 0) ? M : GCD::val }; // { dg-warning "division" "division" } -// { dg-error "not a valid" "valid" { target *-*-* } 6 } +// { dg-error "constant expression" "valid" { target *-*-* } 6 } }; int main() { diff --git a/libstdc++-v3/testsuite/20_util/ratio/cons/cons_overflow_neg.cc b/libstdc++-v3/testsuite/20_util/ratio/cons/cons_overflow_neg.cc index 252265c..c761769 100644 --- a/libstdc++-v3/testsuite/20_util/ratio/cons/cons_overflow_neg.cc +++ b/libstdc++-v3/testsuite/20_util/ratio/cons/cons_overflow_neg.cc @@ -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 } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index e5ea232..bc4eb46 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4882,6 +4882,8 @@ extern void constrain_class_visibility (tree); extern void import_export_decl (tree); extern tree build_cleanup (tree); extern tree build_offset_ref_call_from_tree (tree, VEC(tree,gc) **); +extern bool decl_constant_var_p (tree); +extern bool decl_maybe_constant_var_p (tree); extern void check_default_args (tree); extern void mark_used (tree); extern void finish_static_data_member_decl (tree, tree, bool, tree, int); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 13fa5f6..3f492bb 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -3526,6 +3526,60 @@ decl_defined_p (tree decl) } } +/* Nonzero for a VAR_DECL whose value can be used in a constant expression. + + [expr.const] + + An integral constant-expression can only involve ... const + variables of integral or enumeration types initialized with + constant expressions ... + + C++0x also allows constexpr variables and temporaries initialized + with constant expressions. We handle the former here, but the latter + are just folded away in cxx_eval_constant_expression. + + The standard does not require that the expression be non-volatile. + G++ implements the proposed correction in DR 457. */ + +bool +decl_constant_var_p (tree decl) +{ + bool ret; + tree type = TREE_TYPE (decl); + if (TREE_CODE (decl) != VAR_DECL) + return false; + if (DECL_DECLARED_CONSTEXPR_P (decl)) + ret = true; + else if (CP_TYPE_CONST_NON_VOLATILE_P (type) + && INTEGRAL_OR_ENUMERATION_TYPE_P (type)) + { + /* We don't know if a template static data member is initialized with + a constant expression until we instantiate its initializer. */ + mark_used (decl); + ret = DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl); + } + else + ret = false; + + gcc_assert (!ret || DECL_INITIAL (decl)); + return ret; +} + +/* Returns true if DECL could be a symbolic constant variable, depending on + its initializer. */ + +bool +decl_maybe_constant_var_p (tree decl) +{ + tree type = TREE_TYPE (decl); + if (TREE_CODE (decl) != VAR_DECL) + return false; + if (DECL_DECLARED_CONSTEXPR_P (decl)) + return true; + return (CP_TYPE_CONST_NON_VOLATILE_P (type) + && INTEGRAL_OR_ENUMERATION_TYPE_P (type)); +} + /* Complain that DECL uses a type with no linkage but is never defined. */ static void @@ -4074,8 +4128,6 @@ possibly_inlined_p (tree decl) void mark_used (tree decl) { - HOST_WIDE_INT saved_processing_template_decl = 0; - /* If DECL is a BASELINK for a single function, then treat it just like the DECL for the function. Otherwise, if the BASELINK is for an overloaded function, we don't know which function was @@ -4113,9 +4165,6 @@ mark_used (tree decl) error_at (DECL_SOURCE_LOCATION (decl), "declared here"); return; } - /* If we don't need a value, then we don't need to synthesize DECL. */ - if (cp_unevaluated_operand != 0) - return; /* We can only check DECL_ODR_USED on variables or functions with DECL_LANG_SPECIFIC set, and these are also the only decls that we @@ -4139,31 +4188,39 @@ mark_used (tree decl) return; } - /* Normally, we can wait until instantiation-time to synthesize - DECL. However, if DECL is a static data member initialized with - a constant, we need the value right now because a reference to - such a data member is not value-dependent. */ - if (TREE_CODE (decl) == VAR_DECL - && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) - && DECL_CLASS_SCOPE_P (decl)) + /* Normally, we can wait until instantiation-time to synthesize DECL. + However, if DECL is a static data member initialized with a constant + or a constexpr function, we need it right now because a reference to + such a data member or a call to such function is not value-dependent. */ + if ((decl_maybe_constant_var_p (decl) + || (TREE_CODE (decl) == FUNCTION_DECL + && DECL_DECLARED_CONSTEXPR_P (decl))) + && !DECL_INITIAL (decl) + && DECL_LANG_SPECIFIC (decl) + && DECL_TEMPLATE_INSTANTIATION (decl)) { - /* Don't try to instantiate members of dependent types. We - cannot just use dependent_type_p here because this function - may be called from fold_non_dependent_expr, and then we may - see dependent types, even though processing_template_decl - will not be set. */ - if (CLASSTYPE_TEMPLATE_INFO ((DECL_CONTEXT (decl))) - && uses_template_parms (CLASSTYPE_TI_ARGS (DECL_CONTEXT (decl)))) - return; - /* Pretend that we are not in a template, even if we are, so - that the static data member initializer will be processed. */ - saved_processing_template_decl = processing_template_decl; - processing_template_decl = 0; + /* Instantiating a function will result in garbage collection. We + must treat this situation as if we were within the body of a + function so as to avoid collecting live data only referenced from + the stack (such as overload resolution candidates). */ + ++function_depth; + instantiate_decl (decl, /*defer_ok=*/false, + /*expl_inst_class_mem_p=*/false); + --function_depth; } + /* If we don't need a value, then we don't need to synthesize DECL. */ + if (cp_unevaluated_operand != 0) + return; + if (processing_template_decl) return; + /* Check this too in case we're within fold_non_dependent_expr. */ + if (DECL_TEMPLATE_INFO (decl) + && uses_template_parms (DECL_TI_ARGS (decl))) + return; + DECL_ODR_USED (decl) = 1; if (DECL_CLONED_FUNCTION_P (decl)) DECL_ODR_USED (DECL_CLONED_FUNCTION (decl)) = 1; @@ -4233,8 +4290,6 @@ mark_used (tree decl) need. Therefore, we always try to defer instantiation. */ instantiate_decl (decl, /*defer_ok=*/true, /*expl_inst_class_mem_p=*/false); - - processing_template_decl = saved_processing_template_decl; } #include "gt-cp-decl2.h" diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 8a6d451..0047aae 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -16714,7 +16714,7 @@ always_instantiate_p (tree decl) their initializers are available in integral constant expressions. */ || (TREE_CODE (decl) == VAR_DECL - && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))); + && decl_maybe_constant_var_p (decl))); } /* Produce the definition of D, a _DECL generated from a template. If @@ -16750,7 +16750,8 @@ instantiate_decl (tree d, int defer_ok, case that an expression refers to the value of the variable -- if the variable has a constant value the referring expression can take advantage of that fact. */ - if (TREE_CODE (d) == VAR_DECL) + if (TREE_CODE (d) == VAR_DECL + || DECL_DECLARED_CONSTEXPR_P (d)) defer_ok = 0; /* Don't instantiate cloned functions. Instead, instantiate the @@ -16926,6 +16927,11 @@ instantiate_decl (tree d, int defer_ok, permerror (input_location, "explicit instantiation of %qD " "but no definition available", d); + /* If we're in unevaluated context, we just wanted to get the + constant value; this isn't an odr use, so don't queue + a full instantiation. */ + if (cp_unevaluated_operand != 0) + goto out; /* ??? Historically, we have instantiated inline functions, even when marked as "extern template". */ if (!(external_p && TREE_CODE (d) == VAR_DECL)) diff --git a/gcc/cp/repo.c b/gcc/cp/repo.c index 22d58af..357dcd9 100644 --- a/gcc/cp/repo.c +++ b/gcc/cp/repo.c @@ -319,7 +319,7 @@ repo_emit_p (tree decl) available. Still record them into *.rpo files, so if they weren't actually emitted and collect2 requests them, they can be provided. */ - if (DECL_INTEGRAL_CONSTANT_VAR_P (decl) + if (decl_maybe_constant_var_p (decl) && DECL_CLASS_SCOPE_P (decl)) ret = 2; } diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 5926963..ad26abb 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2826,7 +2826,7 @@ finish_id_expression (tree id_expression, the complexity of the problem" FIXME update for final resolution of core issue 696. */ - if (DECL_INTEGRAL_CONSTANT_VAR_P (decl)) + if (decl_constant_var_p (decl)) return integral_constant_value (decl); if (TYPE_P (context)) @@ -3077,21 +3077,6 @@ finish_id_expression (tree id_expression, return id_expression; } - /* Only certain kinds of names are allowed in constant - expression. Enumerators and template parameters have already - been handled above. */ - if (integral_constant_expression_p - && ! DECL_INTEGRAL_CONSTANT_VAR_P (decl) - && ! builtin_valid_in_constant_expr_p (decl)) - { - if (!allow_non_integral_constant_expression_p) - { - error ("%qD cannot appear in a constant-expression", decl); - return error_mark_node; - } - *non_integral_constant_expression_p = true; - } - if (TREE_CODE (decl) == NAMESPACE_DECL) { error ("use of namespace %qD as expression", decl); @@ -3118,6 +3103,21 @@ finish_id_expression (tree id_expression, || TREE_CODE (decl) == RESULT_DECL) mark_used (decl); + /* Only certain kinds of names are allowed in constant + expression. Enumerators and template parameters have already + been handled above. */ + if (integral_constant_expression_p + && ! decl_constant_var_p (decl) + && ! builtin_valid_in_constant_expr_p (decl)) + { + if (!allow_non_integral_constant_expression_p) + { + error ("%qD cannot appear in a constant-expression", decl); + return error_mark_node; + } + *non_integral_constant_expression_p = true; + } + if (scope) { decl = (adjust_result_of_qualified_name_lookup