From patchwork Tue Jun 29 19:24:51 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 57303 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 D0916B6F01 for ; Wed, 30 Jun 2010 05:25:03 +1000 (EST) Received: (qmail 12307 invoked by alias); 29 Jun 2010 19:25:01 -0000 Received: (qmail 12258 invoked by uid 22791); 29 Jun 2010 19:25:00 -0000 X-SWARE-Spam-Status: No, hits=-5.9 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, 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; Tue, 29 Jun 2010 19:24:54 +0000 Received: from int-mx05.intmail.prod.int.phx2.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.18]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o5TJOqRr023098 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 29 Jun 2010 15:24:52 -0400 Received: from [IPv6:::1] (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx05.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o5TJOpg5007507 for ; Tue, 29 Jun 2010 15:24:51 -0400 Message-ID: <4C2A4883.1070603@redhat.com> Date: Tue, 29 Jun 2010 15:24:51 -0400 From: Jason Merrill User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.10) Gecko/20100619 Lightning/1.0b1 Shredder/3.0.6pre MIME-Version: 1.0 To: gcc-patches List Subject: C++ PATCH for c++/44587 (qualified-ids as template arguments) 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 In 44587, using a qualified-id naming an array static data member as a pointer template argument was causing an ICE. When I investigated this, it turned out that we weren't doing anything about 14.6.2.4/3: A non-integral non-type template-argument is dependent if its type is dependent or it has either of the following forms qualified-id & qualified-id which is somewhat odd because this text was there in C++98. Then I noticed that the equivalent testcase using an unqualified-id was being miscompiled as well--and that it seems to be a hole in the standard, as the address of a static data member is clearly value-dependent, but the standard doesn't say that. These testcases worked in 4.2, but started going wrong in 4.3. So, this patch clarifies that such an address is value-dependent, makes sure that we check that before deciding to fold it, and treats a qualified-id that names a member of the current instantiation the same as an unqualified-id to avoid crashing in lvalue_p. Tested x86_64-pc-linux-gnu, applying to 4.4, 4.5 and trunk. commit af6c6141ae0833fe8cb7bebda2196e9c382cd074 Author: Jason Merrill Date: Mon Jun 28 18:11:13 2010 -0400 PR c++/44587 * pt.c (has_value_dependent_address): New. (value_dependent_expression_p): Check it. (convert_nontype_argument): Likewise. Call decay_conversion before folding if we want a pointer. * semantics.c (finish_id_expression): Don't add SCOPE_REF if the scope is the current instantiation. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 863218d..8a447ec 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -4880,6 +4880,36 @@ check_valid_ptrmem_cst_expr (tree type, tree expr) return false; } +/* Returns TRUE iff the address of OP is value-dependent. + + 14.6.2.4 [temp.dep.temp]: + A non-integral non-type template-argument is dependent if its type is + dependent or it has either of the following forms + qualified-id + & qualified-id + and contains a nested-name-specifier which specifies a class-name that + names a dependent type. + + We generalize this to just say that the address of a member of a + dependent class is value-dependent; the above doesn't cover the + address of a static data member named with an unqualified-id. */ + +static bool +has_value_dependent_address (tree op) +{ + /* We could use get_inner_reference here, but there's no need; + this is only relevant for template non-type arguments, which + can only be expressed as &id-expression. */ + if (DECL_P (op)) + { + tree ctx = CP_DECL_CONTEXT (op); + if (TYPE_P (ctx) && dependent_type_p (ctx)) + return true; + } + + return false; +} + /* Attempt to convert the non-type template parameter EXPR to the indicated TYPE. If the conversion is successful, return the converted value. If the conversion is unsuccessful, return @@ -4918,6 +4948,11 @@ convert_nontype_argument (tree type, tree expr) return NULL_TREE; } + /* Add the ADDR_EXPR now for the benefit of + value_dependent_expression_p. */ + if (TYPE_PTROBV_P (type)) + expr = decay_conversion (expr); + /* If we are in a template, EXPR may be non-dependent, but still have a syntactic, rather than semantic, form. For example, EXPR might be a SCOPE_REF, rather than the VAR_DECL to which the @@ -4925,7 +4960,11 @@ convert_nontype_argument (tree type, tree expr) so that access checking can be performed when the template is instantiated -- but here we need the resolved form so that we can convert the argument. */ - expr = fold_non_dependent_expr (expr); + if (TYPE_REF_OBJ_P (type) + && has_value_dependent_address (expr)) + /* If we want the address and it's value-dependent, don't fold. */; + else + expr = fold_non_dependent_expr (expr); if (error_operand_p (expr)) return error_mark_node; expr_type = TREE_TYPE (expr); @@ -17638,6 +17677,13 @@ value_dependent_expression_p (tree expression) return ((value_dependent_expression_p (TREE_OPERAND (expression, 0))) || (value_dependent_expression_p (TREE_OPERAND (expression, 2)))); + case ADDR_EXPR: + { + tree op = TREE_OPERAND (expression, 0); + return (value_dependent_expression_p (op) + || has_value_dependent_address (op)); + } + default: /* A constant expression is value-dependent if any subexpression is value-dependent. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index e750937..8baf76a 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3097,7 +3097,13 @@ finish_id_expression (tree id_expression, { tree r = convert_from_reference (decl); - if (processing_template_decl && TYPE_P (scope)) + /* In a template, return a SCOPE_REF for most qualified-ids + so that we can check access at instantiation time. But if + we're looking at a member of the current instantiation, we + know we have access and building up the SCOPE_REF confuses + non-type template argument handling. */ + if (processing_template_decl && TYPE_P (scope) + && !currently_open_class (scope)) r = build_qualified_name (TREE_TYPE (r), scope, decl, template_p); diff --git a/gcc/testsuite/g++.dg/template/qualified-id2.C b/gcc/testsuite/g++.dg/template/qualified-id2.C new file mode 100644 index 0000000..e88e854 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/qualified-id2.C @@ -0,0 +1,27 @@ +// PR c++/44587 +// { dg-do run } + +template struct A { static const char *p; }; +template const char *A::p = N; +template struct B { static const char c[1]; typedef A::c> C; }; +template const char B::c[1] = ""; +template struct D { static const char c[1]; typedef A C; }; +template const char D::c[1] = ""; + +template struct E { static int *ip; }; +template int* E::ip = &I; +template struct F { static int i; typedef E::i> C; }; +template int F::i; +template struct G { static int i; typedef E C; }; +template int G::i; + +#define AS(X) if (!(X)) return 1; +int main() +{ + AS(B::C::p == B::c); + AS(B::C::p == B::c); + AS(B::C::p != B::c); + AS(D::C::p == D::c); + AS(D::C::p == D::c); + AS(D::C::p != D::c); +} diff --git a/gcc/testsuite/g++.dg/template/qualified-id3.C b/gcc/testsuite/g++.dg/template/qualified-id3.C new file mode 100644 index 0000000..d97ef5c --- /dev/null +++ b/gcc/testsuite/g++.dg/template/qualified-id3.C @@ -0,0 +1,14 @@ +// PR c++/44587 + +template struct A { }; +template struct B { + static const int c; + typedef A::c> C; // { dg-error "non-constant" } +}; +template const int B::c = sizeof (T); + +template struct D { }; +template struct E { + static const int c = sizeof (T); + typedef D::c> F; // OK +};