From patchwork Wed Oct 27 19:31:15 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 69393 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 D054CB6EED for ; Thu, 28 Oct 2010 06:31:34 +1100 (EST) Received: (qmail 27496 invoked by alias); 27 Oct 2010 19:31:32 -0000 Received: (qmail 27411 invoked by uid 22791); 27 Oct 2010 19:31:28 -0000 X-SWARE-Spam-Status: No, hits=-6.0 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, TW_CX, TW_FN, TW_KF, TW_SF, 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; Wed, 27 Oct 2010 19:31:18 +0000 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o9RJVH7p025791 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 27 Oct 2010 15:31:17 -0400 Received: from [127.0.0.1] (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o9RJVFT0019270 for ; Wed, 27 Oct 2010 15:31:16 -0400 Message-ID: <4CC87E03.2070005@redhat.com> Date: Wed, 27 Oct 2010 15:31:15 -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 PATCH #2: improve type checking of constexpr declarations 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 This patch improves our type checking of constexpr declarations: variables must have literal type, functions must have parameter and return types of literal or reference type, constructors must have an empty body and have mem-initializers for any bases or members. The patch also adds various syntax and redeclaration checks. Tested x86_64-pc-linux-gnu, applied to trunk. commit 93c3af7c9f729c6675598c9e47e11b3e2b25673a Author: Jason Merrill Date: Tue Oct 26 18:28:45 2010 -0400 * parser.c (cp_parser_ctor_initializer_opt_and_function_body): Make sure a constexpr ctor has an empty body. * class.c (type_has_constexpr_default_constructor): New. * cp-tree.h: Declare it. * init.c (perform_member_init): Complain about uninitialized member in constexpr ctor. (emit_mem_initializers): And uninitialized base. * decl.c (check_tag_decl): Fix typo. * semantics.c (valid_type_in_constexpr_fundecl_p): New fn. (is_valid_constexpr_fn): New fn. (validate_constexpr_fundecl): Use it. * decl.c (validate_constexpr_redeclaration): New. (duplicate_decls): Use it. (cp_finish_decl): Call validate_constexpr_fundecl and ensure_literal_type_for_constexpr_object here. (start_decl): Not here. Don't ICE on constexpr reference. (check_for_uninitialized_const_var): Don't handle constexpr specially. (grokfndecl): Set DECL_DECLARED_CONSTEXPR_P. (check_static_variable_definition): Give friendly message about missing constexpr. (grokdeclarator): Complain about typedef and volatile with constexpr. Reorganize. Give sorry about non-static data members in C++0x mode. (start_preparsed_function): Check validate_constexpr_fundecl here. (check_function_type): Not here. * decl2.c (finish_static_data_member_decl): Don't complain about in-class init. * parser.c (CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR): New. (cp_parser_condition): Pass it to cp_parser_decl_specifier_seq. (cp_parser_decl_specifier_seq): Handle it. (cp_parser_explicit_instantiation): Diagnose inline and constexpr. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 00af0ae..c3e3c53 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -4325,6 +4325,19 @@ type_has_user_provided_default_constructor (tree t) return false; } +/* Returns true iff class T has a constexpr default constructor. */ + +bool +type_has_constexpr_default_constructor (tree t) +{ + tree fns; + + if (!CLASS_TYPE_P (t)) + return false; + fns = get_default_ctor (t); + return (fns && DECL_DECLARED_CONSTEXPR_P (fns)); +} + /* Returns true iff class TYPE has a virtual destructor. */ bool diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 8c0c9b1..1cd776a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4721,6 +4721,7 @@ extern tree in_class_defaulted_default_constructor (tree); extern bool user_provided_p (tree); extern bool type_has_user_provided_constructor (tree); extern bool type_has_user_provided_default_constructor (tree); +extern bool type_has_constexpr_default_constructor (tree); extern bool type_has_virtual_destructor (tree); extern bool type_has_move_constructor (tree); extern bool type_has_move_assign (tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index e27a64d..8b2af9cd 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1128,6 +1128,32 @@ check_redeclaration_exception_specification (tree new_decl, } } +/* Return true if OLD_DECL and NEW_DECL agree on constexprness. + Otherwise issue diagnostics. */ + +static bool +validate_constexpr_redeclaration (tree old_decl, tree new_decl) +{ + old_decl = STRIP_TEMPLATE (old_decl); + new_decl = STRIP_TEMPLATE (new_decl); + if (!VAR_OR_FUNCTION_DECL_P (old_decl) + || !VAR_OR_FUNCTION_DECL_P (new_decl)) + return true; + if (DECL_DECLARED_CONSTEXPR_P (old_decl) + == DECL_DECLARED_CONSTEXPR_P (new_decl)) + return true; + if (TREE_CODE (old_decl) == FUNCTION_DECL && DECL_BUILT_IN (old_decl)) + { + /* Hide a built-in declaration. */ + DECL_DECLARED_CONSTEXPR_P (old_decl) + = DECL_DECLARED_CONSTEXPR_P (new_decl); + return true; + } + error ("redeclaration %qD differs in %", new_decl); + error ("from previous declaration %q+D", old_decl); + return false; +} + #define GNU_INLINE_P(fn) (DECL_DECLARED_INLINE_P (fn) \ && lookup_attribute ("gnu_inline", \ DECL_ATTRIBUTES (fn))) @@ -1607,6 +1633,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) warn about it. */ warn_extern_redeclared_static (newdecl, olddecl); + if (!validate_constexpr_redeclaration (olddecl, newdecl)) + return error_mark_node; + /* We have committed to returning 1 at this point. */ if (TREE_CODE (newdecl) == FUNCTION_DECL) { @@ -4029,7 +4058,7 @@ check_tag_decl (cp_decl_specifier_seq *declspecs) else if (saw_typedef) warning (0, "% was ignored in this declaration"); else if (declspecs->specs[(int) ds_constexpr]) - error ("% cannot be used for type declarations"); + error ("% cannot be used for type declarations"); } return declared_type; @@ -4310,9 +4339,6 @@ start_decl (const cp_declarator *declarator, && !alias) permerror (input_location, "declaration of %q#D outside of class is not definition", decl); - - if (!ensure_literal_type_for_constexpr_object (decl)) - return error_mark_node; } was_public = TREE_PUBLIC (decl); @@ -4344,7 +4370,7 @@ start_decl (const cp_declarator *declarator, /* This is a const variable with implicit 'static'. Set DECL_THIS_STATIC so we can tell it from variables that are !TREE_PUBLIC because of the anonymous namespace. */ - gcc_assert (CP_TYPE_CONST_P (TREE_TYPE (decl))); + gcc_assert (CP_TYPE_CONST_P (TREE_TYPE (decl)) || errorcount); DECL_THIS_STATIC (decl) = 1; } @@ -4753,14 +4779,10 @@ check_for_uninitialized_const_var (tree decl) { tree type = strip_array_types (TREE_TYPE (decl)); - if (TREE_CODE (decl) == VAR_DECL && DECL_DECLARED_CONSTEXPR_P (decl) - && DECL_INITIAL (decl) == NULL) - error ("missing initializer for constexpr %qD", decl); - /* ``Unless explicitly declared extern, a const object does not have external linkage and must be initialized. ($8.4; $12.1)'' ARM 7.1.6 */ - else if (TREE_CODE (decl) == VAR_DECL + if (TREE_CODE (decl) == VAR_DECL && TREE_CODE (type) != REFERENCE_TYPE && CP_TYPE_CONST_P (type) && (!TYPE_NEEDS_CONSTRUCTING (type) @@ -5691,6 +5713,12 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, } } + if (TREE_CODE (decl) == FUNCTION_DECL) + validate_constexpr_fundecl (decl); + + else if (!ensure_literal_type_for_constexpr_object (decl)) + DECL_DECLARED_CONSTEXPR_P (decl) = 0; + if (init && TREE_CODE (decl) == FUNCTION_DECL) { tree clone; @@ -6959,6 +6987,8 @@ grokfndecl (tree ctype, /* If the declaration was declared inline, mark it as such. */ if (inlinep) DECL_DECLARED_INLINE_P (decl) = 1; + if (inlinep & 2) + DECL_DECLARED_CONSTEXPR_P (decl) = true; DECL_EXTERNAL (decl) = 1; if (quals && TREE_CODE (type) == FUNCTION_TYPE) @@ -7341,6 +7371,21 @@ build_ptrmem_type (tree class_type, tree member_type) int check_static_variable_definition (tree decl, tree type) { + /* If DECL is declared constexpr, we'll do the appropriate checks + in check_initializer. */ + if (DECL_P (decl) && DECL_DECLARED_CONSTEXPR_P (decl)) + return 0; + else if (cxx_dialect >= cxx0x && !INTEGRAL_OR_ENUMERATION_TYPE_P (type)) + { + if (literal_type_p (type)) + error ("% needed for in-class initialization of static " + "data member %q#D of non-integral type", decl); + else + error ("in-class initialization of static data member %q#D of " + "non-literal type", decl); + return 1; + } + /* Motion 10 at San Diego: If a static const integral data member is initialized with an integral constant expression, the initializer may appear either in the declaration (within the class), or in @@ -7352,10 +7397,6 @@ check_static_variable_definition (tree decl, tree type) error ("invalid in-class initialization of static data member " "of non-integral type %qT", type); - /* If we just return the declaration, crashes will sometimes - occur. We therefore return void_type_node, as if this were a - friend declaration, to cause callers to completely ignore - this declaration. */ return 1; } else if (!CP_TYPE_CONST_P (type)) @@ -8046,6 +8087,12 @@ grokdeclarator (const cp_declarator *declarator, if (name == NULL) name = decl_context == PARM ? "parameter" : "type name"; + if (constexpr_p && declspecs->specs[(int)ds_typedef]) + { + error ("% cannot appear in a typedef declaration"); + return error_mark_node; + } + /* If there were multiple types specified in the decl-specifier-seq, issue an error message. */ if (declspecs->multiple_types_p) @@ -8299,17 +8346,6 @@ grokdeclarator (const cp_declarator *declarator, type_quals = TYPE_UNQUALIFIED; if (declspecs->specs[(int)ds_const]) type_quals |= TYPE_QUAL_CONST; - /* A `constexpr' specifier used in an object declaration declares - the object as `const'. */ - if (constexpr_p) - { - if (innermost_code == cdk_function) - ; - else if (declspecs->specs[(int)ds_const] != 0) - error ("both % and % cannot be used here"); - else - type_quals |= TYPE_QUAL_CONST; - } if (declspecs->specs[(int)ds_volatile]) type_quals |= TYPE_QUAL_VOLATILE; if (declspecs->specs[(int)ds_restrict]) @@ -8686,21 +8722,6 @@ grokdeclarator (const cp_declarator *declarator, } } - /* It is not allowed to use `constexpr' in a function - declaration that is not a definition. - That is too strict, though. */ - if (constexpr_p && !funcdef_flag) - { - error ("the % specifier cannot be used in " - "a function declaration that is not a definition"); - constexpr_p = false; - } - - /* A constexpr non-static member function is implicitly const. */ - if (constexpr_p && decl_context == FIELD && staticp == 0 - && sfk != sfk_constructor && sfk != sfk_destructor) - memfn_quals |= TYPE_QUAL_CONST; - arg_types = grokparms (declarator->u.function.parameters, &parms); @@ -8878,6 +8899,18 @@ grokdeclarator (const cp_declarator *declarator, } } + /* A `constexpr' specifier used in an object declaration declares + the object as `const'. */ + if (constexpr_p && innermost_code != cdk_function) + { + if (type_quals & TYPE_QUAL_CONST) + error ("both % and % cannot be used here"); + if (type_quals & TYPE_QUAL_VOLATILE) + error ("both % and % cannot be used here"); + type_quals |= TYPE_QUAL_CONST; + type = cp_build_qualified_type (type, type_quals); + } + if (unqualified_id && TREE_CODE (unqualified_id) == TEMPLATE_ID_EXPR && TREE_CODE (type) != FUNCTION_TYPE && TREE_CODE (type) != METHOD_TYPE) @@ -8964,8 +8997,6 @@ grokdeclarator (const cp_declarator *declarator, return error_mark_node; else if (TREE_CODE (type) == FUNCTION_TYPE) { - tree sname = declarator->u.id.unqualified_name; - if (current_class_type && (!friendp || funcdef_flag)) { @@ -8975,20 +9006,6 @@ grokdeclarator (const cp_declarator *declarator, ctype, name, current_class_type); return error_mark_node; } - - /* It is not permitted to define a member function outside ist - membership class as `constexpr'. */ - if (constexpr_p) - error ("a constexpr function cannot be defined " - "outside of its class"); - - if (TREE_CODE (sname) == IDENTIFIER_NODE - && NEW_DELETE_OPNAME_P (sname)) - /* Overloaded operator new and operator delete - are always static functions. */ - ; - else - type = build_memfn_type (type, ctype, memfn_quals); } else if (declspecs->specs[(int)ds_typedef] && current_class_type) @@ -8999,6 +9016,15 @@ grokdeclarator (const cp_declarator *declarator, } } + if (ctype == NULL_TREE && decl_context == FIELD && friendp == 0) + ctype = current_class_type; + + /* A constexpr non-static member function is implicitly const. */ + if (constexpr_p && ctype && staticp == 0 + && TREE_CODE (type) == FUNCTION_TYPE + && sfk != sfk_constructor && sfk != sfk_destructor) + memfn_quals |= TYPE_QUAL_CONST; + /* Now TYPE has the actual type. */ if (returned_attrs) @@ -9362,6 +9388,10 @@ grokdeclarator (const cp_declarator *declarator, type = build_pointer_type (type); } + if (ctype && TREE_CODE (type) == FUNCTION_TYPE && staticp < 2 + && !NEW_DELETE_OPNAME_P (unqualified_id)) + type = build_memfn_type (type, ctype, memfn_quals); + { tree decl; @@ -9395,22 +9425,15 @@ grokdeclarator (const cp_declarator *declarator, error ("invalid use of %<::%>"); return error_mark_node; } - else if (TREE_CODE (type) == FUNCTION_TYPE) + else if (TREE_CODE (type) == FUNCTION_TYPE + || TREE_CODE (type) == METHOD_TYPE) { int publicp = 0; tree function_context; if (friendp == 0) { - if (ctype == NULL_TREE) - ctype = current_class_type; - - if (ctype == NULL_TREE) - { - error ("can't make %qD into a method -- not in a class", - unqualified_id); - return error_mark_node; - } + gcc_assert (ctype); /* ``A union may [ ... ] not [ have ] virtual functions.'' ARM 9.5 */ @@ -9431,8 +9454,6 @@ grokdeclarator (const cp_declarator *declarator, virtualp = 0; } } - else if (staticp < 2) - type = build_memfn_type (type, ctype, memfn_quals); } /* Check that the name used for a destructor makes sense. */ @@ -9455,9 +9476,12 @@ grokdeclarator (const cp_declarator *declarator, return error_mark_node; } if (constexpr_p) - error ("a destructor cannot be %"); + { + error ("a destructor cannot be %"); + return error_mark_node; + } } - else if (sfk == sfk_constructor && friendp) + else if (sfk == sfk_constructor && friendp && !ctype) { error ("expected qualified name in friend declaration " "for constructor %qD", @@ -9477,7 +9501,7 @@ grokdeclarator (const cp_declarator *declarator, unqualified_id, virtualp, flags, memfn_quals, raises, friendp ? -1 : 0, friendp, publicp, - inlinep || constexpr_p, + inlinep | (2 * constexpr_p), sfk, funcdef_flag, template_count, in_namespace, attrlist, declarator->id_loc); @@ -9499,25 +9523,6 @@ grokdeclarator (const cp_declarator *declarator, if (explicitp == 2) DECL_NONCONVERTING_P (decl) = 1; } - else if (TREE_CODE (type) == METHOD_TYPE) - { - /* We only get here for friend declarations of - members of other classes. */ - /* All method decls are public, so tell grokfndecl to set - TREE_PUBLIC, also. */ - decl = grokfndecl (ctype, type, - TREE_CODE (unqualified_id) != TEMPLATE_ID_EXPR - ? unqualified_id : dname, - parms, - unqualified_id, - virtualp, flags, memfn_quals, raises, - friendp ? -1 : 0, friendp, 1, 0, sfk, - funcdef_flag, template_count, in_namespace, - attrlist, - declarator->id_loc); - if (decl == NULL_TREE) - return error_mark_node; - } else if (!staticp && !dependent_type_p (type) && !COMPLETE_TYPE_P (complete_type (type)) && (TREE_CODE (type) != ARRAY_TYPE || initialized == 0)) @@ -9596,15 +9601,25 @@ grokdeclarator (const cp_declarator *declarator, the rest of the compiler does not correctly handle the initialization unless the member is static so we make it static below. */ - permerror (input_location, "ISO C++ forbids initialization of member %qD", - unqualified_id); - permerror (input_location, "making %qD static", unqualified_id); - staticp = 1; + if (cxx_dialect >= cxx0x) + { + sorry ("non-static data member initializers"); + } + else + { + permerror (input_location, "ISO C++ forbids initialization of member %qD", + unqualified_id); + permerror (input_location, "making %qD static", unqualified_id); + staticp = 1; + } } if (uses_template_parms (type)) /* We'll check at instantiation time. */ ; + else if (constexpr_p) + /* constexpr has the same requirements. */ + ; else if (check_static_variable_definition (unqualified_id, type)) /* If we just return the declaration, crashes @@ -9714,11 +9729,6 @@ grokdeclarator (const cp_declarator *declarator, sfk = sfk_none; } } - else if (TREE_CODE (type) == FUNCTION_TYPE && staticp < 2 - && !NEW_DELETE_OPNAME_P (original_name)) - type = build_method_type_directly (ctype, - TREE_TYPE (type), - TYPE_ARG_TYPES (type)); /* Record presence of `static'. */ publicp = (ctype != NULL_TREE @@ -9728,7 +9738,8 @@ grokdeclarator (const cp_declarator *declarator, decl = grokfndecl (ctype, type, original_name, parms, unqualified_id, virtualp, flags, memfn_quals, raises, 1, friendp, - publicp, inlinep || constexpr_p, sfk, funcdef_flag, + publicp, inlinep | (2 * constexpr_p), sfk, + funcdef_flag, template_count, in_namespace, attrlist, declarator->id_loc); if (decl == NULL_TREE) @@ -9797,6 +9808,9 @@ grokdeclarator (const cp_declarator *declarator, storage_class = sc_none; } } + else if (constexpr_p && DECL_EXTERNAL (decl)) + error ("declaration of constexpr variable %qD is not a definition", + decl); } if (storage_class == sc_extern && initialized && !funcdef_flag) @@ -9826,8 +9840,8 @@ grokdeclarator (const cp_declarator *declarator, DECL_THIS_STATIC (decl) = 1; /* Don't forget constexprness. */ - if (VAR_OR_FUNCTION_DECL_P (decl)) - DECL_DECLARED_CONSTEXPR_P (decl) = constexpr_p; + if (constexpr_p) + DECL_DECLARED_CONSTEXPR_P (decl) = true; /* Record constancy and volatility on the DECL itself . There's no need to do this when processing a template; we'll do this @@ -11863,10 +11877,6 @@ check_function_type (tree decl, tree current_function_parms) /* In a function definition, arg types must be complete. */ require_complete_types_for_parms (current_function_parms); - /* constexpr functions must have literal argument types and - literal return type. */ - validate_constexpr_fundecl (decl); - if (dependent_type_p (return_type)) return; if (!COMPLETE_OR_VOID_TYPE_P (return_type) @@ -12126,6 +12136,10 @@ start_preparsed_function (tree decl1, tree attrs, int flags) maybe_apply_pragma_weak (decl1); } + /* constexpr functions must have literal argument types and + literal return type. */ + validate_constexpr_fundecl (decl1); + /* Reset this in case the call to pushdecl changed it. */ current_function_decl = decl1; diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index f27e7d6..13fa5f6 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -771,21 +771,6 @@ finish_static_data_member_decl (tree decl, permerror (input_location, "local class %q#T shall not have static data member %q#D", current_class_type, decl); - /* Static consts need not be initialized in the class definition. */ - if (init != NULL_TREE && TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))) - { - static int explained = 0; - - error ("initializer invalid for static member with constructor"); - if (!explained) - { - error ("(an out of class initialization is required)"); - explained = 1; - } - init = NULL_TREE; - } - - DECL_INITIAL (decl) = init; DECL_IN_AGGR_P (decl) = 1; if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 9c2afba..3a6e2e7 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -533,6 +533,15 @@ perform_member_init (tree member, tree init) "uninitialized member %qD with % type %qT", member, type); + if (DECL_DECLARED_CONSTEXPR_P (current_function_decl) + && !type_has_constexpr_default_constructor (type)) + { + if (!DECL_TEMPLATE_INSTANTIATION (current_function_decl)) + error ("uninitialized member %qD in % constructor", + member); + DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false; + } + core_type = strip_array_types (type); if (CLASS_TYPE_P (core_type) && (CLASSTYPE_READONLY_FIELDS_NEED_INIT (core_type) @@ -864,17 +873,30 @@ emit_mem_initializers (tree mem_inits) tree subobject = TREE_PURPOSE (mem_inits); tree arguments = TREE_VALUE (mem_inits); - /* If these initializations are taking place in a copy constructor, - the base class should probably be explicitly initialized if there - is a user-defined constructor in the base class (other than the - default constructor, which will be called anyway). */ - if (extra_warnings && !arguments - && DECL_COPY_CONSTRUCTOR_P (current_function_decl) - && type_has_user_nondefault_constructor (BINFO_TYPE (subobject))) - warning_at (DECL_SOURCE_LOCATION (current_function_decl), OPT_Wextra, - "base class %q#T should be explicitly initialized in the " - "copy constructor", - BINFO_TYPE (subobject)); + if (arguments == NULL_TREE) + { + /* If these initializations are taking place in a copy constructor, + the base class should probably be explicitly initialized if there + is a user-defined constructor in the base class (other than the + default constructor, which will be called anyway). */ + if (extra_warnings + && DECL_COPY_CONSTRUCTOR_P (current_function_decl) + && type_has_user_nondefault_constructor (BINFO_TYPE (subobject))) + warning_at (DECL_SOURCE_LOCATION (current_function_decl), + OPT_Wextra, "base class %q#T should be explicitly " + "initialized in the copy constructor", + BINFO_TYPE (subobject)); + + if (DECL_DECLARED_CONSTEXPR_P (current_function_decl) + && !(type_has_constexpr_default_constructor + (BINFO_TYPE (subobject)))) + { + if (!DECL_TEMPLATE_INSTANTIATION (current_function_decl)) + error ("uninitialized base %qT in % constructor", + BINFO_TYPE (subobject)); + DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false; + } + } /* Initialize the base. */ if (BINFO_VIRTUAL_P (subobject)) diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index d0bd6bb..0d28345 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -1334,7 +1334,10 @@ enum CP_PARSER_FLAGS_NO_USER_DEFINED_TYPES = 0x2, /* When parsing a type-specifier, do not try to parse a class-specifier or enum-specifier. */ - CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS = 0x4 + CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS = 0x4, + /* When parsing a decl-specifier-seq, only allow type-specifier or + constexpr. */ + CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8 }; /* This type is used for parameters and variables which hold @@ -8509,6 +8512,7 @@ cp_parser_condition (cp_parser* parser) { cp_decl_specifier_seq type_specifiers; const char *saved_message; + int declares_class_or_enum; /* Try the declaration first. */ cp_parser_parse_tentatively (parser); @@ -8518,9 +8522,10 @@ cp_parser_condition (cp_parser* parser) parser->type_definition_forbidden_message = G_("types may not be defined in conditions"); /* Parse the type-specifier-seq. */ - cp_parser_type_specifier_seq (parser, /*is_declaration==*/true, - /*is_trailing_return=*/false, - &type_specifiers); + cp_parser_decl_specifier_seq (parser, + CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR, + &type_specifiers, + &declares_class_or_enum); /* Restore the saved message. */ parser->type_definition_forbidden_message = saved_message; /* If all is well, we might be looking at a declaration. */ @@ -9851,6 +9856,11 @@ cp_parser_decl_specifier_seq (cp_parser* parser, break; } + if (found_decl_spec + && (flags & CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR) + && token->keyword != RID_CONSTEXPR) + error ("decl-specifier invalid in condition"); + /* Constructors are a special case. The `S' in `S()' is not a decl-specifier; it is the beginning of the declarator. */ constructor_p @@ -12231,6 +12241,13 @@ cp_parser_explicit_instantiation (cp_parser* parser) decl_specifiers.type_location); if (declarator != cp_error_declarator) { + if (decl_specifiers.specs[(int)ds_inline]) + permerror (input_location, "explicit instantiation shall not use" + " % specifier"); + if (decl_specifiers.specs[(int)ds_constexpr]) + permerror (input_location, "explicit instantiation shall not use" + " % specifier"); + decl = grokdeclarator (declarator, &decl_specifiers, NORMAL, 0, &decl_specifiers.attributes); /* Turn access control back on for names used during @@ -16245,15 +16262,43 @@ cp_parser_function_body (cp_parser *parser) static bool cp_parser_ctor_initializer_opt_and_function_body (cp_parser *parser) { - tree body; + tree body, list; bool ctor_initializer_p; + const bool check_body_p = + DECL_CONSTRUCTOR_P (current_function_decl) + && DECL_DECLARED_CONSTEXPR_P (current_function_decl); + tree last = NULL; /* Begin the function body. */ body = begin_function_body (); /* Parse the optional ctor-initializer. */ ctor_initializer_p = cp_parser_ctor_initializer_opt (parser); + + /* If we're parsing a constexpr constructor definition, we need + to check that the constructor body is indeed empty. However, + before we get to cp_parser_function_body lot of junk has been + generated, so we can't just check that we have an empty block. + Rather we take a snapshot of the outermost block, and check whether + cp_parser_function_body changed its state. */ + if (check_body_p) + { + list = body; + if (TREE_CODE (list) == BIND_EXPR) + list = BIND_EXPR_BODY (list); + if (TREE_CODE (list) == STATEMENT_LIST + && STATEMENT_LIST_TAIL (list) != NULL) + last = STATEMENT_LIST_TAIL (list)->stmt; + } /* Parse the function-body. */ cp_parser_function_body (parser); + if (check_body_p + && (TREE_CODE (list) != STATEMENT_LIST + || (last == NULL && STATEMENT_LIST_TAIL (list) != NULL) + || (last != NULL && last != STATEMENT_LIST_TAIL (list)->stmt))) + { + error ("constexpr constructor does not have empty body"); + DECL_DECLARED_CONSTEXPR_P (current_function_decl) = false; + } /* Finish the function body. */ finish_function_body (body); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 4e73068..0ca8c33 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -5277,6 +5277,59 @@ ensure_literal_type_for_constexpr_object (tree decl) return decl; } +/* Return true if type expression T is a valid parameter type, or + a valid return type, of a constexpr function. */ + +static bool +valid_type_in_constexpr_fundecl_p (tree t) +{ + return (literal_type_p (t) + /* FIXME we allow ref to non-literal; should change standard to + match, or change back if not. */ + || TREE_CODE (t) == REFERENCE_TYPE); +} + +/* Check whether the parameter and return types of FUN are valid for a + constexpr function, and complain if COMPLAIN. */ + +static bool +is_valid_constexpr_fn (tree fun, bool complain) +{ + tree parm = FUNCTION_FIRST_USER_PARM (fun); + bool ret = true; + for (; parm != NULL; parm = TREE_CHAIN (parm)) + if (!valid_type_in_constexpr_fundecl_p (TREE_TYPE (parm))) + { + ret = false; + if (complain) + error ("invalid type for parameter %q#D of constexpr function", + parm); + } + + if (!DECL_CONSTRUCTOR_P (fun)) + { + tree rettype = TREE_TYPE (TREE_TYPE (fun)); + if (!valid_type_in_constexpr_fundecl_p (rettype)) + { + ret = false; + if (complain) + error ("invalid return type %qT of constexpr function %qD", + rettype, fun); + } + + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun) + && COMPLETE_TYPE_P (DECL_CONTEXT (fun)) + && !valid_type_in_constexpr_fundecl_p (DECL_CONTEXT (fun))) + { + ret = false; + if (complain) + error ("enclosing class of %q#D is not a literal type", fun); + } + } + + return ret; +} + /* Return non-null if FUN certainly designates a valid constexpr function declaration. Otherwise return NULL. Issue appropriate diagnostics if necessary. Note that we only check the declaration, not the body @@ -5285,43 +5338,18 @@ ensure_literal_type_for_constexpr_object (tree decl) tree validate_constexpr_fundecl (tree fun) { - tree rettype = NULL; - tree parm = NULL; - - /* Don't bother if FUN is not marked constexpr. */ - if (!DECL_DECLARED_CONSTEXPR_P (fun)) + if (processing_template_decl || !DECL_DECLARED_CONSTEXPR_P (fun)) return NULL; - - /* For a function template, we have absolutely no guarantee that all - instantiations will be constexpr. */ - if (TREE_CODE (fun) == TEMPLATE_DECL) - return NULL; - - parm = FUNCTION_FIRST_USER_PARM (fun); - for (; parm != NULL; parm = TREE_CHAIN (parm)) - { - tree type = TREE_TYPE (parm); - if (dependent_type_p (type)) - return NULL; - if (!literal_type_p (type)) - { - error ("parameter %q#D is not of literal type", parm); - return NULL; - } - } - - if (DECL_CONSTRUCTOR_P (fun)) + else if (DECL_CLONED_FUNCTION_P (fun)) + /* We already checked the original function. */ return fun; - rettype = TREE_TYPE (TREE_TYPE (fun)); - if (dependent_type_p (rettype)) - return NULL; - if (!literal_type_p (rettype)) + if (!is_valid_constexpr_fn (fun, !DECL_TEMPLATE_INSTANTIATION (fun))) { - error ("return type %qT of function %qD is not a literal type", - TREE_TYPE (TREE_TYPE (fun)), fun); + DECL_DECLARED_CONSTEXPR_P (fun) = false; return NULL; } + return fun; } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-auto.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-auto.C new file mode 100644 index 0000000..ddf0da0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-auto.C @@ -0,0 +1,2 @@ +// { dg-options -std=c++0x } +constexpr auto value = 0; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-condition.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-condition.C new file mode 100644 index 0000000..e2328fc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-condition.C @@ -0,0 +1,9 @@ +// { dg-options -std=c++0x } +// Core DR 948 + +constexpr int something() { return 3; } + +int main() { + if (constexpr long v = something()) {} + if (static long v = something()) { } // { dg-error "decl-specifier invalid" } +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C new file mode 100644 index 0000000..91c489d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C @@ -0,0 +1,7 @@ +// { dg-options -std=c++0x } + +struct A +{ + int i; + constexpr A() { } // { dg-error "uninitialized member .A::i" } +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-expinst.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-expinst.C new file mode 100644 index 0000000..2089873 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-expinst.C @@ -0,0 +1,5 @@ +// { dg-options -std=c++0x } +// Error: Explicit instantiation of a function template shall not use the +// inline or constexpr specifiers +template constexpr inline T bar(T x) { return x; } +template constexpr inline float bar(float x); // { dg-error "specifier" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice2.C new file mode 100644 index 0000000..35643b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice2.C @@ -0,0 +1,3 @@ +// { dg-options -std=c++0x } +int x; +constexpr int& rx = x; // { dg-error "int&" }