From patchwork Wed May 24 11:34:16 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathan Sidwell X-Patchwork-Id: 766441 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]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wXr0x0ZNlz9s8N for ; Wed, 24 May 2017 21:35:11 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="sHtkIvHb"; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; q=dns; s=default; b=ic/ola+6s8SRFPm3CzEqCI6Pjck0poWypMxoI4LGIY1FXauk1d KuleVXc/bIsEafxkXnsDjqbIc5YSIoAdazgbbdWXkbQsLwSasWTi9DVOtqfcaehG hr7/Qy/ZZyLu1/5nGjkqC0+27cjSFu9JoK1L+TXAeP0D0yQYbmhGlePAE= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; s= default; bh=YEviVZPgy0clgODWdOhgL7kGPBA=; b=sHtkIvHbgLuMCYK5ZkQZ 0tNmN4ltEgqnBhd7tK3uD0gvH869fvQ7guGy8ixHMQcTlJ+9huMvCTDqZX9CjTJa EUmB1r5iIUvYgVUR1H2K2OEq+kPUAupwawwSKZV/agxihwU+LbtuKYh5aYi6nMU0 XIcYqdBKLLOF0KNCOATh0UE= Received: (qmail 70980 invoked by alias); 24 May 2017 11:34:20 -0000 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 Received: (qmail 70949 invoked by uid 89); 24 May 2017 11:34:19 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-10.6 required=5.0 tests=BAYES_00, FREEMAIL_FROM, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=ham version=3.3.2 spammy=teaches, bob X-HELO: mail-yb0-f172.google.com Received: from mail-yb0-f172.google.com (HELO mail-yb0-f172.google.com) (209.85.213.172) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 24 May 2017 11:34:17 +0000 Received: by mail-yb0-f172.google.com with SMTP id 187so38246149ybg.0 for ; Wed, 24 May 2017 04:34:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:to:from:subject:message-id:date :user-agent:mime-version:content-language; bh=sNrbMHGvILzUTkXotrpaqEFZkeSXyX2FHAWAYSKz5YU=; b=DqcB9BHKSAdXhPbMech8bIa4oWJZszHg6RpZv4jnzA16dc3Dz8nfsCP6Mnrl8cjtTJ r31W5O4PWq0IoqqVF8VvwUVyKEgwYrMIXEVOWU32SdWy8h/OfZgT0epR89vuDbt5HyQg jKOCxnS8UsvgrxOHQt+eNzQbL/BD7dyrYj/egXmjZfPLvMywa441qu1ZhUbgxo9G26jX zNcngIXx67aCXhsxbZua7CRr71r/mjvF9ELnk4q63fnL7Cg+5p0aUgkhknj873eY8u1J /3YC2aaR7G1S0lJk96rU62xgG81rwn5+n5SnXBrzVWjGHvw8BlvWQ/3/vsRd0LYApZVu Qv7A== X-Gm-Message-State: AODbwcAlRl7ObCrHzSLNKSogrbvFbdNj8Dkf2i/1P5pO9smjNRZPxep/ YBhragt4VSC3Ag== X-Received: by 10.37.29.86 with SMTP id d83mr24379423ybd.146.1495625659102; Wed, 24 May 2017 04:34:19 -0700 (PDT) Received: from ?IPv6:2620:10d:c0a1:1102:b9b0:1beb:d21c:b3f4? ([2620:10d:c091:180::1:11db]) by smtp.googlemail.com with ESMTPSA id v77sm1599232ywc.6.2017.05.24.04.34.18 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 24 May 2017 04:34:18 -0700 (PDT) To: GCC Patches From: Nathan Sidwell Subject: [C++ PATCH] Hidden functions Message-ID: <3b33777d-ac48-f85c-ca47-f0d25cf26a06@acm.org> Date: Wed, 24 May 2017 07:34:16 -0400 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.1.0 MIME-Version: 1.0 We have functions that are invisible to (most) name lookups: 1) anticipated builtins -- these only become visible once declared, but we need to know they match a builtin. 2) friends injected into namespace scope from a class definition. This patch teaches the overload object about them with a new OVL_HIDDEN_P marker (so we can just look at the overload node), and more importantly it keeps them at the start of the overload list. That'll make removing them from lookup results much simpler (a future patch). Because of the ordering constraint, revealing a node is no longer so simple. We may have to delete and reinsert it. Thus a check after duplicate_decls discovers we're (re-)declaring a previously hidden decl. The new testcases are to do with such unhiding. anticipated builtins are always "C" functions, we need to do the check about extern "C" declarations matching. nathan 2017-05-24 Nathan Sidwell gcc/cp/ * cp-tree.h (OVL_HIDDEN_P): New. (ovl_iterator::hidden_p, ovl_iterator::reveal_node): New. (ovl_iterator::reveal_node): Declare. * tree.c (ovl_copy): Copy OVL_HIDDEN_P. (ovl_insert): Order on hiddenness. (ovl_iterator::reveal_node): New. * name-lookup.c (anticipated_builtin_p): New. (supplement_binding_1): Use it. (set_local_extern_decl_linkage): Use hidden_p. (do_pushdecl): Deal with unhiding a hidden decl, use anticipated_builtin_p. (do_nonmember_using_decl): Use anticipated_decl_p. (lookup_name_real_1): Use DECL_HIDDEN_P. gcc/testsuite/ * g++.dg/lookup/extern-c-hidden.C: New. * g++.dg/lookup/extern-redecl1.C: New. Index: cp/cp-tree.h =================================================================== --- cp/cp-tree.h (revision 248387) +++ cp/cp-tree.h (working copy) @@ -378,6 +378,7 @@ extern GTY(()) tree cp_global_trees[CPTI REF_PARENTHESIZED_P (in COMPONENT_REF, INDIRECT_REF, SCOPE_REF) AGGR_INIT_ZERO_FIRST (in AGGR_INIT_EXPR) CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR) + OVL_HIDDEN_P (in OVERLOAD) 3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out). ICS_BAD_FLAG (in _CONV) FN_TRY_BLOCK_P (in TRY_BLOCK) @@ -659,6 +660,8 @@ typedef struct ptrmem_cst * ptrmem_cst_t /* If set, this was imported in a using declaration. */ #define OVL_USING_P(NODE) TREE_LANG_FLAG_1 (OVERLOAD_CHECK (NODE)) +/* If set, this overload is a hidden decl. */ +#define OVL_HIDDEN_P(NODE) TREE_LANG_FLAG_2 (OVERLOAD_CHECK (NODE)) /* If set, this overload contains a nested overload. */ #define OVL_NESTED_P(NODE) TREE_LANG_FLAG_3 (OVERLOAD_CHECK (NODE)) /* If set, this overload was constructed during lookup. */ @@ -729,15 +732,26 @@ class ovl_iterator { return TREE_CODE (ovl) == OVERLOAD && OVL_USING_P (ovl); } + bool hidden_p () const + { + return TREE_CODE (ovl) == OVERLOAD && OVL_HIDDEN_P (ovl); + } + + public: tree remove_node (tree head) { return remove_node (head, ovl); } + tree reveal_node (tree head) + { + return reveal_node (head, ovl); + } private: - /* We make this a static function to avoid the address of the + /* We make these static functions to avoid the address of the iterator escaping the local context. */ static tree remove_node (tree head, tree node); + static tree reveal_node (tree ovl, tree node); }; /* Iterator over a (potentially) 2 dimensional overload, which is Index: cp/name-lookup.c =================================================================== --- cp/name-lookup.c (revision 248387) +++ cp/name-lookup.c (working copy) @@ -1077,6 +1077,26 @@ strip_using_decl (tree decl) return decl; } +/* Return true if OVL is an overload for an anticipated builtin. */ + +static bool +anticipated_builtin_p (tree ovl) +{ + if (TREE_CODE (ovl) != OVERLOAD) + return false; + + if (!OVL_HIDDEN_P (ovl)) + return false; + + tree fn = OVL_FUNCTION (ovl); + gcc_checking_assert (DECL_ANTICIPATED (fn)); + + if (DECL_HIDDEN_FRIEND_P (fn)) + return false; + + return true; +} + /* BINDING records an existing declaration for a name in the current scope. But, DECL is another declaration for that same identifier in the same scope. This is the `struct stat' hack whereby a non-typedef @@ -1131,9 +1151,7 @@ supplement_binding_1 (cxx_binding *bindi || target_bval == error_mark_node /* If TARGET_BVAL is anticipated but has not yet been declared, pretend it is not there at all. */ - || (TREE_CODE (target_bval) == FUNCTION_DECL - && DECL_ANTICIPATED (target_bval) - && !DECL_HIDDEN_FRIEND_P (target_bval))) + || anticipated_builtin_p (target_bval)) binding->value = decl; else if (TREE_CODE (target_bval) == TYPE_DECL && DECL_ARTIFICIAL (target_bval) @@ -1803,7 +1821,7 @@ set_local_extern_decl_linkage (tree decl loc_value = NULL_TREE; for (ovl_iterator iter (loc_value); iter; ++iter) - if (!DECL_HIDDEN_P (*iter) + if (!iter.hidden_p () && (TREE_STATIC (*iter) || DECL_EXTERNAL (*iter)) && decls_match (*iter, decl)) { @@ -1931,15 +1949,34 @@ do_pushdecl (tree decl, bool is_friend) if (iter.using_p ()) ; /* Ignore using decls here. */ else if (tree match = duplicate_decls (decl, *iter, is_friend)) - return match; + { + if (iter.hidden_p () + && match != error_mark_node + && !DECL_HIDDEN_P (match)) + { + /* Unhiding a previously hidden decl. */ + tree head = iter.reveal_node (old); + if (head != old) + { + if (!ns) + update_local_overload (binding, head); + binding->value = head; + } + + if (TREE_CODE (match) == FUNCTION_DECL + && DECL_EXTERN_C_P (match)) + /* We need to check and register the fn now. */ + check_extern_c_conflict (match); + } + return match; + } /* We are pushing a new decl. */ - /* Skip a hidden builtin we failed to match already. */ - if (old && TREE_CODE (old) == FUNCTION_DECL - && DECL_ANTICIPATED (old) - && !DECL_HIDDEN_FRIEND_P (old)) - old = NULL_TREE; + /* Skip a hidden builtin we failed to match already. There can + only be one. */ + if (old && anticipated_builtin_p (old)) + old = OVL_CHAIN (old); check_template_shadow (decl); @@ -3045,8 +3082,7 @@ do_nonmember_using_decl (tree scope, tre found = true; else if (old.using_p ()) continue; /* This is a using decl. */ - else if (DECL_ANTICIPATED (old_fn) - && !DECL_HIDDEN_FRIEND_P (old_fn)) + else if (old.hidden_p () && !DECL_HIDDEN_FRIEND_P (old_fn)) continue; /* This is an anticipated builtin. */ else if (!matching_fn_p (new_fn, old_fn)) continue; /* Parameters do not match. */ @@ -3069,9 +3105,7 @@ do_nonmember_using_decl (tree scope, tre } else if (value /* Ignore anticipated builtins. */ - && !(TREE_CODE (value) == FUNCTION_DECL - && DECL_ANTICIPATED (value) - && !DECL_HIDDEN_FRIEND_P (value)) + && !anticipated_builtin_p (value) && !decls_match (lookup.value, value)) diagnose_name_conflict (lookup.value, value); else @@ -5244,7 +5278,7 @@ lookup_name_real_1 (tree name, int prefe if (binding) { - if (hidden_name_p (binding)) + if (TREE_CODE (binding) == TYPE_DECL && DECL_HIDDEN_P (binding)) { /* A non namespace-scope binding can only be hidden in the presence of a local class, due to friend declarations. Index: cp/tree.c =================================================================== --- cp/tree.c (revision 248386) +++ cp/tree.c (working copy) @@ -2143,7 +2143,8 @@ ovl_copy (tree ovl) TREE_TYPE (result) = TREE_TYPE (ovl); OVL_FUNCTION (result) = OVL_FUNCTION (ovl); OVL_CHAIN (result) = OVL_CHAIN (ovl); - OVL_USING_P (ovl) = OVL_USING_P (ovl); + OVL_HIDDEN_P (result) = OVL_HIDDEN_P (ovl); + OVL_USING_P (result) = OVL_USING_P (ovl); return result; } @@ -2156,14 +2157,16 @@ tree ovl_insert (tree fn, tree maybe_ovl, bool using_p) { bool copying = false; /* Checking use only. */ - int weight = using_p; + bool hidden_p = DECL_HIDDEN_P (fn); + int weight = (hidden_p << 1) | (using_p << 0); tree result = NULL_TREE; tree insert_after = NULL_TREE; /* Find insertion point. */ while (maybe_ovl && TREE_CODE (maybe_ovl) == OVERLOAD - && (weight < OVL_USING_P (maybe_ovl))) + && (weight < ((OVL_HIDDEN_P (maybe_ovl) << 1) + | (OVL_USING_P (maybe_ovl) << 0)))) { gcc_checking_assert (!OVL_LOOKUP_P (maybe_ovl) && (!OVL_USED_P (maybe_ovl) || !copying)); @@ -2181,9 +2184,11 @@ ovl_insert (tree fn, tree maybe_ovl, boo } tree trail = fn; - if (maybe_ovl || using_p || TREE_CODE (fn) == TEMPLATE_DECL) + if (maybe_ovl || using_p || hidden_p || TREE_CODE (fn) == TEMPLATE_DECL) { trail = ovl_make (fn, maybe_ovl); + if (hidden_p) + OVL_HIDDEN_P (trail) = true; if (using_p) OVL_USING_P (trail) = true; } @@ -2199,6 +2204,28 @@ ovl_insert (tree fn, tree maybe_ovl, boo return result; } +/* NODE is an OVL_HIDDEN_P node which is now revealed. */ + +tree +ovl_iterator::reveal_node (tree overload, tree node) +{ + /* We cannot have returned NODE as part of a lookup overload, so it + cannot be USED. */ + gcc_checking_assert (!OVL_USED_P (node)); + + OVL_HIDDEN_P (node) = false; + if (tree chain = OVL_CHAIN (node)) + if (TREE_CODE (chain) == OVERLOAD + && (OVL_USING_P (chain) || OVL_HIDDEN_P (chain))) + { + /* The node needs moving, and the simplest way is to remove it + and reinsert. */ + overload = remove_node (overload, node); + overload = ovl_insert (OVL_FUNCTION (node), overload); + } + return overload; +} + /* NODE is on the overloads of OVL. Remove it. If a predecessor is OVL_USED_P we must copy OVL nodes, because those are immutable. The removed node is unaltered and may continue to be iterated Index: testsuite/g++.dg/lookup/extern-c-hidden.C =================================================================== --- testsuite/g++.dg/lookup/extern-c-hidden.C (revision 0) +++ testsuite/g++.dg/lookup/extern-c-hidden.C (working copy) @@ -0,0 +1,11 @@ +// Make sure unhidding an extern-c still checks it is compatible + +extern "C" float fabsf (float); // { dg-error "conflicts with previous declaration" } + +namespace Bob +{ + extern "C" float fabsf (float, float); // { dg-error "C language" } + extern "C" double fabs (double, double); // { dg-error "conflicts with previous declaration" } +} + +extern "C" double fabs (double); // { dg-error "C language" } Index: testsuite/g++.dg/lookup/extern-redecl1.C =================================================================== --- testsuite/g++.dg/lookup/extern-redecl1.C (revision 0) +++ testsuite/g++.dg/lookup/extern-redecl1.C (working copy) @@ -0,0 +1,18 @@ +extern int X; // { dg-message "previous declaration" } +extern int Y (int); // { dg-message "previous declaration" } +extern int Y (float); + +static int Z (int s) +{ + return s; +} + +void Foo () +{ + extern char X; // { dg-error "local external declaration" } + extern char Y (int); // { dg-error "local external declaration" } + extern int Y (float); + extern void Y (double); + extern char Z (int); +} +