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
(In reply to Martin Nowak from comment #6) > Could anyone Digger the commit that introduced the problem? https://github.com/dlang/dmd/pull/5472
Comment #8 by code — 2016-05-03T08:28:59Z
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
Comment #13 by code — 2016-05-03T09:16:00Z
Comment #14 by k.hara.pg — 2016-05-04T07:41:43Z
(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
Commits pushed to stable at https://github.com/dlang/dmd https://github.com/dlang/dmd/commit/b6dba3105957cce41ddeb2af77991ef58f82d552 fix Issue 15897 - private base class method not seen through derived class - properly deprecate private symbol visibility through derived classes https://github.com/dlang/dmd/commit/c41f00bd16297c5855ed21de72d2e63aa8fc08e5 Merge pull request #5727 from MartinNowak/fix15897 fix Issue 15897 - private base class method not seen through derived class
Comment #16 by k.hara.pg — 2016-05-07T10:32:38Z
The new concept https://issues.dlang.org/show_bug.cgi?id=15897#c12 is completely new thing, so language spec needs to be updated.
Comment #17 by github-bugzilla — 2016-05-16T02:51:27Z
Commits pushed to master at https://github.com/dlang/dmd https://github.com/dlang/dmd/commit/b6dba3105957cce41ddeb2af77991ef58f82d552 fix Issue 15897 - private base class method not seen through derived class https://github.com/dlang/dmd/commit/c41f00bd16297c5855ed21de72d2e63aa8fc08e5 Merge pull request #5727 from MartinNowak/fix15897
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. ***