Bug 15897 – private base class method not seen through derived class
Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2016-04-08T13:04:00Z
Last change time
2016-09-12T03:36:35Z
Keywords
pull
Assigned to
nobody
Creator
schveiguy
Comments
Comment #0 by schveiguy — 2016-04-08T13:04:21Z
Example (from https://forum.dlang.org/post/[email protected])
module a;
import b;
class Animal
{
private void create() {}
}
void foo(Cat cat)
{
cat.create(); // >> no property create for type 'b.cat'
}
void main() {}
--------------
module b;
import a;
class Cat: Animal {}
Compiles with 2.070
Fails in 2.071.0:
Error: no property 'create' for type 'b.Cat'
If I do this:
void foo(Cat cat)
{
Animal a = cat;
a.create();
}
It now compiles. The user shouldn't have to jump through this hoop, the compiler is aware of the access of create via the base class.
If you move Cat into the same module, it also compiles.
Comment #1 by bugzilla — 2016-04-15T10:57:05Z
Neither are bugs, they are expected behavior.
Ordinarily, this would be removed by making Animal a 'private' base class. But that was removed for reasons I don't understand.
Comment #2 by schveiguy — 2016-04-15T14:50:44Z
Can you point me at the rationale for why this change was made?
From your explanation, I don't know if you understand what is happening here.
Comment #3 by code — 2016-04-16T09:16:07Z
Yes I think that should compil, just like this works.
----
private void create(Animal animal) {}
class Animal
{
}
void foo(Cat cat)
{
cat.create(); // >> no property create for type 'b.cat'
}
----
Comment #4 by schveiguy — 2016-04-16T13:08:06Z
(In reply to Martin Nowak from comment #3)
> Yes I think that should compil, just like this works.
> ...
> cat.create(); // >> no property create for type 'b.cat'
The comment implies that it doesn't compile, but I tested and it does.
Another case:
private void create(Animal animal) { import std.stdio; writeln("ufcs"); }
class Animal
{
void create() { import std.stdio; writeln("member"); }
}
void main() { foo(new Cat); }
prints: ufcs
In 2.070.2 it prints: member
Comment #5 by bugzilla — 2016-04-22T00:17:59Z
(In reply to Steven Schveighoffer from comment #4)
> private void create(Animal animal) { import std.stdio; writeln("ufcs"); }
>
> class Animal
> {
> void create() { import std.stdio; writeln("member"); }
> }
>
> void main() { foo(new Cat); }
>
> prints: ufcs
>
> In 2.070.2 it prints: member
That's what everyone has been asking for.
Comment #6 by code — 2016-04-22T07:18:57Z
Interestingly this only fails when compiling both modules or only module b, but not when only compiling module a.
It also fails w/ a public instead of a private create method, so this is likely a forward reference issue due to the circular import. At best you resolve the issue by taking the base class (Animal) as parameter to foo, rather than the imported derived class (Cat).
Could anyone Digger the commit that introduced the problem?
Comment #7 by dlang-bugzilla — 2016-04-22T07:30:04Z
My initial intuition was wrong here. A private method in the base class should not be visible from a derived class, even in the base class' module.
Even in the base class' module we filter visibility through the derived class for the following reasons.
- You can introduce a create method in the derived class which would not conflict w/ the base class method, but hijack the call in your example.
- The derived class would "look" differently in different modules, depending on all of it's base classes. So far we only make a distinction between the class' module/package and other modules.
To make your code work you have to explicitly convert the derived class to the base class you want to access,
// qualified base class access
cat.Animal.create();
// even better, get rid of the circular import
void foo(Animal animal)
{
animal.create();
}
Comment #9 by dlang-bugzilla — 2016-05-03T08:32:35Z
(In reply to Martin Nowak from comment #8)
> A private method in the base class
> should not be visible from a derived class, even in the base class' module.
Why is that? private in D is file-level, not declaration-level.
Comment #10 by dlang-bugzilla — 2016-05-03T08:35:29Z
The spec says:
"Private means that only members of the enclosing class can access the member, or members and functions in the same module as the enclosing class."
-- https://dlang.org/spec/attribute.html#protection_attributes
TDPL says:
"In all contexts, private has the same power: it restricts
symbol access to the current module (file)."
Comment #11 by dlang-bugzilla — 2016-05-03T08:37:38Z
(In reply to Vladimir Panteleev from comment #9)
> (In reply to Martin Nowak from comment #8)
> > A private method in the base class
> > should not be visible from a derived class, even in the base class' module.
>
> Why is that?
Sorry, somehow missed your explanation.
It still contradicts the spec and TDPL though.
Comment #12 by code — 2016-05-03T09:13:43Z
Yeah, we introduced sort of a new concept, "visibility through something", i.e. with the following import chain pkg.A -> B -> pkg.C, pkg.A cannot see package(pkg) protected symbols from pkg.C, even when B publically imports pkg.C.
The same applies for base classes.
Visibility can only be restricted but not be widened later on, which is a fairly intuitive behavior and follows the same principle as the overload resolution w/ mixed protection, making it mostly independent from lookup origin.
http://wiki.dlang.org/DIP22#Description
(In reply to Martin Nowak from comment #12)
> Yeah, we introduced sort of a new concept, "visibility through something",
> i.e. with the following import chain pkg.A -> B -> pkg.C, pkg.A cannot see
> package(pkg) protected symbols from pkg.C, even when B publically imports
> pkg.C.
> The same applies for base classes.
>
> Visibility can only be restricted but not be widened later on, which is a
> fairly intuitive behavior and follows the same principle as the overload
> resolution w/ mixed protection, making it mostly independent from lookup
> origin.
> http://wiki.dlang.org/DIP22#Description
How the new concept works for aliased symbols *through* template alias parameters? For example:
module a;
import b;
class C
{
private int a;
}
void test()
{
auto c = new C();
assert(c.a == 0);
Foo!c.foo();
assert(c.a == 1);
}
module b;
template Foo(alias var)
{
void foo() { var.a = 1; } // accessing private symbol via alias parameter
}
I thought it could be accepted case, but won't it be accepted anymore?
Comment #15 by github-bugzilla — 2016-05-06T20:38:28Z
Comment #18 by meapineapple — 2016-06-08T10:26:24Z
Isn't this what the "protected" access modifier is for?
Comment #19 by code — 2016-09-12T03:20:26Z
(In reply to Sophie from comment #18)
> Isn't this what the "protected" access modifier is for?
Yes, protected would be the choice for inheriting a function.
Here the question was whether a private member of a base class, should be visible for derived classes in the base class' module. Not revealing that private member, b/c the derived class is in a different module, is a semantic change introduced by DIP22, hence the deprecation.
Comment #20 by code — 2016-09-12T03:36:35Z
*** Issue 15983 has been marked as a duplicate of this issue. ***