c++: private inheritance access diagnostics fix [PR17314]

This patch fixes PR17314. Previously, when class C attempted
to access member a declared in class A through class B, where class B
privately inherits from A and class C inherits from B, GCC would correctly
report an access violation, but would erroneously report that the reason was
because a was "protected", when in fact, from the point of view of class C,
it was really "private". This patch updates the diagnostics code to generate
more correct errors in cases of failed inheritance such as these.

The reason this bug happened was because GCC was examining the
declared access of decl, instead of looking at it in the
context of class inheritance.

gcc/cp/ChangeLog:

2021-01-21  Anthony Sharp  <anthonysharp15@gmail.com>

	* call.c (complain_about_access): Altered function.
	* cp-tree.h (complain_about_access): Changed parameters of function.
	(get_parent_with_private_access): Declared new function.
	* search.c (get_parent_with_private_access): Defined new function.
	* semantics.c (enforce_access): Modified function.
	* typeck.c (complain_about_unrecognized_member): Updated function
	arguments in complain_about_access.

gcc/testsuite/ChangeLog:

2021-01-21  Anthony Sharp  <anthonysharp15@gmail.com>

	* g++.dg/lookup/scoped1.C: Modified testcase to run successfully
	with changes.
	* g++.dg/tc1/dr142.C: Same as above.
	* g++.dg/tc1/dr52.C: Same as above.
	* g++.old-deja/g++.brendan/visibility6.C: Same as above.
	* g++.old-deja/g++.brendan/visibility8.C: Same as above.
	* g++.old-deja/g++.jason/access8.C: Same as above.
	* g++.old-deja/g++.law/access4.C: Same as above.
	* g++.old-deja/g++.law/visibility12.C: Same as above.
	* g++.old-deja/g++.law/visibility4.C: Same as above.
	* g++.old-deja/g++.law/visibility8.C: Same as above.
	* g++.old-deja/g++.other/access4.C: Same as above.
This commit is contained in:
Anthony Sharp 2021-01-22 22:36:06 +00:00 committed by Jason Merrill
parent c63f091db8
commit 7e0f147a29
16 changed files with 129 additions and 39 deletions

View File

@ -7142,27 +7142,45 @@ build_op_delete_call (enum tree_code code, tree addr, tree size,
/* Issue diagnostics about a disallowed access of DECL, using DIAG_DECL /* Issue diagnostics about a disallowed access of DECL, using DIAG_DECL
in the diagnostics. in the diagnostics.
If ISSUE_ERROR is true, then issue an error about the If ISSUE_ERROR is true, then issue an error about the access, followed
access, followed by a note showing the declaration. by a note showing the declaration. Otherwise, just show the note.
Otherwise, just show the note. */
DIAG_DECL and DIAG_LOCATION will almost always be the same.
DIAG_LOCATION is just another DECL. NO_ACCESS_REASON is an optional
parameter used to specify why DECL wasn't accessible (e.g. ak_private
would be because DECL was private). If not using NO_ACCESS_REASON,
then it must be ak_none, and the access failure reason will be
figured out by looking at the protection of DECL. */
void void
complain_about_access (tree decl, tree diag_decl, bool issue_error) complain_about_access (tree decl, tree diag_decl, tree diag_location,
bool issue_error, access_kind no_access_reason)
{ {
if (TREE_PRIVATE (decl)) /* If we have not already figured out why DECL is inaccessible... */
if (no_access_reason == ak_none)
{
/* Examine the access of DECL to find out why. */
if (TREE_PRIVATE (decl))
no_access_reason = ak_private;
else if (TREE_PROTECTED (decl))
no_access_reason = ak_protected;
}
/* Now generate an error message depending on calculated access. */
if (no_access_reason == ak_private)
{ {
if (issue_error) if (issue_error)
error ("%q#D is private within this context", diag_decl); error ("%q#D is private within this context", diag_decl);
inform (DECL_SOURCE_LOCATION (diag_decl), inform (DECL_SOURCE_LOCATION (diag_location), "declared private here");
"declared private here");
} }
else if (TREE_PROTECTED (decl)) else if (no_access_reason == ak_protected)
{ {
if (issue_error) if (issue_error)
error ("%q#D is protected within this context", diag_decl); error ("%q#D is protected within this context", diag_decl);
inform (DECL_SOURCE_LOCATION (diag_decl), inform (DECL_SOURCE_LOCATION (diag_location), "declared protected here");
"declared protected here");
} }
/* Couldn't figure out why DECL is inaccesible, so just say it's
inaccessible. */
else else
{ {
if (issue_error) if (issue_error)

View File

@ -6434,7 +6434,8 @@ class access_failure_info
tree m_diag_decl; tree m_diag_decl;
}; };
extern void complain_about_access (tree, tree, bool); extern void complain_about_access (tree, tree, tree, bool,
access_kind);
extern void push_defarg_context (tree); extern void push_defarg_context (tree);
extern void pop_defarg_context (void); extern void pop_defarg_context (void);
extern tree convert_default_arg (tree, tree, tree, int, extern tree convert_default_arg (tree, tree, tree, int,
@ -7274,6 +7275,7 @@ extern unsigned get_pseudo_tinfo_index (tree);
extern tree get_pseudo_tinfo_type (unsigned); extern tree get_pseudo_tinfo_type (unsigned);
/* in search.c */ /* in search.c */
extern tree get_parent_with_private_access (tree decl, tree binfo);
extern bool accessible_base_p (tree, tree, bool); extern bool accessible_base_p (tree, tree, bool);
extern tree lookup_base (tree, tree, base_access, extern tree lookup_base (tree, tree, base_access,
base_kind *, tsubst_flags_t); base_kind *, tsubst_flags_t);

View File

@ -122,6 +122,41 @@ dfs_lookup_base (tree binfo, void *data_)
return NULL_TREE; return NULL_TREE;
} }
/* This deals with bug PR17314.
DECL is a declaration and BINFO represents a class that has attempted (but
failed) to access DECL.
Examine the parent binfos of BINFO and determine whether any of them had
private access to DECL. If they did, return the parent binfo. This helps
in figuring out the correct error message to show (if the parents had
access, it's their fault for not giving sufficient access to BINFO).
If no parents had access, return NULL_TREE. */
tree
get_parent_with_private_access (tree decl, tree binfo)
{
/* Only BINFOs should come through here. */
gcc_assert (TREE_CODE (binfo) == TREE_BINFO);
tree base_binfo = NULL_TREE;
/* Iterate through immediate parent classes. */
for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
{
/* This parent had private access. Therefore that's why BINFO can't
access DECL. */
if (access_in_type (BINFO_TYPE (base_binfo), decl) == ak_private)
return base_binfo;
}
/* None of the parents had access. Note: it's impossible for one of the
parents to have had public or protected access to DECL, since then
BINFO would have been able to access DECL too. */
return NULL_TREE;
}
/* Returns true if type BASE is accessible in T. (BASE is known to be /* Returns true if type BASE is accessible in T. (BASE is known to be
a (possibly non-proper) base class of T.) If CONSIDER_LOCAL_P is a (possibly non-proper) base class of T.) If CONSIDER_LOCAL_P is
true, consider any special access of the current scope, or access true, consider any special access of the current scope, or access

View File

@ -316,7 +316,36 @@ enforce_access (tree basetype_path, tree decl, tree diag_decl,
if (flag_new_inheriting_ctors) if (flag_new_inheriting_ctors)
diag_decl = strip_inheriting_ctors (diag_decl); diag_decl = strip_inheriting_ctors (diag_decl);
if (complain & tf_error) if (complain & tf_error)
complain_about_access (decl, diag_decl, true); {
/* We will usually want to point to the same place as
diag_decl but not always. */
tree diag_location = diag_decl;
access_kind parent_access = ak_none;
/* See if any of BASETYPE_PATH's parents had private access
to DECL. If they did, that will tell us why we don't. */
tree parent_binfo = get_parent_with_private_access (decl,
basetype_path);
/* If a parent had private access, then the diagnostic
location DECL should be that of the parent class, since it
failed to give suitable access by using a private
inheritance. But if DECL was actually defined in the parent,
it wasn't privately inherited, and so we don't need to do
this, and complain_about_access will figure out what to
do. */
if (parent_binfo != NULL_TREE
&& (context_for_name_lookup (decl)
!= BINFO_TYPE (parent_binfo)))
{
diag_location = TYPE_NAME (BINFO_TYPE (parent_binfo));
parent_access = ak_private;
}
/* Finally, generate an error message. */
complain_about_access (decl, diag_decl, diag_location, true,
parent_access);
}
if (afi) if (afi)
afi->record_access_failure (basetype_path, decl, diag_decl); afi->record_access_failure (basetype_path, decl, diag_decl);
return false; return false;

View File

@ -2980,7 +2980,8 @@ complain_about_unrecognized_member (tree access_path, tree name,
TREE_CODE (access_path) == TREE_BINFO TREE_CODE (access_path) == TREE_BINFO
? TREE_TYPE (access_path) : object_type, ? TREE_TYPE (access_path) : object_type,
name, afi.get_diag_decl ()); name, afi.get_diag_decl ());
complain_about_access (afi.get_decl (), afi.get_diag_decl (), false); complain_about_access (afi.get_decl (), afi.get_diag_decl (),
afi.get_diag_decl (), false, ak_none);
} }
} }
else else

View File

@ -4,12 +4,12 @@
struct A struct A
{ {
static int i1; static int i1;
int i2; // { dg-message "declared" } int i2;
static void f1 (); static void f1 ();
void f2 (); void f2 ();
}; };
struct B: private A { }; struct B: private A { }; // { dg-message "declared" }
struct C: public B struct C: public B
{ {
void g () void g ()

View File

@ -2,13 +2,13 @@
// Origin: Giovanni Bajo <giovannibajo at gcc dot gnu dot org> // Origin: Giovanni Bajo <giovannibajo at gcc dot gnu dot org>
// DR142: Injection-related errors in access example // DR142: Injection-related errors in access example
class B { // { dg-message "declared" } class B {
public: public:
int mi; // { dg-message "declared" } int mi;
static int si; // { dg-message "declared" } static int si;
}; };
class D: private B { class D: private B { // { dg-message "declared" }
}; };
class DD: public D { class DD: public D {

View File

@ -17,11 +17,11 @@ struct B1 : B {};
struct B2 : B {}; struct B2 : B {};
struct C struct C
{ // { dg-message "declared" } {
void foo(void); void foo(void);
}; };
struct D : private C {}; struct D : private C {}; // { dg-message "declared" }
struct X: A, B1, B2, D struct X: A, B1, B2, D
{ {

View File

@ -3,9 +3,9 @@
class bottom class bottom
{ {
public: public:
int b; // { dg-message "" } private int b;
}; };
class middle : private bottom class middle : private bottom // { dg-message "" } private
{ {
public: public:
void foo () { b; } void foo () { b; }

View File

@ -5,9 +5,9 @@
class foo class foo
{ {
public: public:
static int y; // { dg-message "" } private static int y;
}; };
class foo1 : private foo class foo1 : private foo // { dg-message "" } private
{ }; { };
class foo2 : public foo1 class foo2 : public foo1
{ public: { public:

View File

@ -3,13 +3,14 @@
// Date: 25 Jan 1994 23:41:33 -0500 // Date: 25 Jan 1994 23:41:33 -0500
// Bug: g++ forgets access decls after the definition. // Bug: g++ forgets access decls after the definition.
class inh { // { dg-message "" } inaccessible class inh {
int a; int a;
protected: protected:
void myf(int); void myf(int);
}; };
class mel : private inh { class mel : private inh // { dg-message "" } inaccessible
{
protected: protected:
int t; int t;
inh::myf; // { dg-warning "deprecated" } inh::myf; // { dg-warning "deprecated" }

View File

@ -6,12 +6,13 @@
// Subject: g++ 2.5.5 doesn't warn about inaccessible virtual base ctor // Subject: g++ 2.5.5 doesn't warn about inaccessible virtual base ctor
// Message-ID: <9403030024.AA04534@ses.com> // Message-ID: <9403030024.AA04534@ses.com>
class ForceLeafSterile { // { dg-message "" } class ForceLeafSterile {
friend class Sterile; friend class Sterile;
ForceLeafSterile() {} // { dg-message "" } ForceLeafSterile() {} // { dg-message "" }
}; };
class Sterile : private virtual ForceLeafSterile { class Sterile : private virtual ForceLeafSterile // { dg-message "" }
{
public: public:
Sterile() {} Sterile() {}
Sterile(const char* /*blah*/) {} Sterile(const char* /*blah*/) {}

View File

@ -6,11 +6,12 @@
// Subject: member access rule bug // Subject: member access rule bug
// Message-ID: <9306300528.AA17185@coda.mel.dit.CSIRO.AU> // Message-ID: <9306300528.AA17185@coda.mel.dit.CSIRO.AU>
struct a { struct a {
int aa; // { dg-message "" } private int aa;
}; };
class b : private a { class b : private a // { dg-message "" } private
}; {
};
class c : public b { class c : public b {
int xx(void) { return (aa); } // aa should be invisible// { dg-error "" } .* int xx(void) { return (aa); } // aa should be invisible// { dg-error "" } .*

View File

@ -8,10 +8,11 @@
class A { class A {
public: public:
int b; // { dg-message "" } private int b;
}; };
class C : private A { // NOTE WELL. private, not public class C : private A // { dg-message "" } private
{
public: public:
int d; int d;
}; };

View File

@ -7,11 +7,12 @@
// Message-ID: <m0nof3E-0021ifC@jts.com // Message-ID: <m0nof3E-0021ifC@jts.com
class t1 { class t1 {
protected: protected:
int a; // { dg-message "" } protected int a;
}; };
class t2 : private t1 { class t2 : private t1 // { dg-message "" } protected
{
public: public:
int b; int b;
}; };

View File

@ -1,10 +1,10 @@
// { dg-do assemble } // { dg-do assemble }
struct A { // { dg-message "" } inaccessible struct A {
static int i; static int i;
}; };
struct B : private A { }; struct B : private A { }; // { dg-message "" } inaccessible
struct C : public B { struct C : public B {
int f () { return A::i; } // { dg-error "" } context int f () { return A::i; } // { dg-error "" } context