-
Notifications
You must be signed in to change notification settings - Fork 421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How do methods only from the type definition point interact with shadowing? #19352
Comments
Relevant spec sections are https://chapel-lang.org/docs/latest/language/spec/methods.html#method-calls and https://chapel-lang.org/docs/language/spec/procedures.html#determining-visible-functions . The determining visible functions section should be updated to point to the other (or have a sentence). Neither say anything about shadowing. |
I'm not sure I agree with that, but it might be that I did something which caused it. I think if a type defines a primary or secondary method, that method should always be found (discussions of privacy aside). That doesn't say anything about its precedence relative to other symbols. Personally, I think things provided by the definition point of the type should take precedence when a clear equivalence is known. Doing otherwise would enable hijacking and go against the intentions of the creator of the type. The one place that is somewhat questionable to me is references within a method on the type that could correspond to a method name or could correspond to a function name, e.g. proc symbolB() { ... }
proc Foo.methodA(...) {
symbolB(); // Foo has a method named symbolB, which should be chosen?
// The user should be able to write something to make a choice, though, e.g. `Module.symbolB` or `this.symbolB`
}
I don't think it should - tertiary methods should be defined to be complementary to the primary and secondary methods, enabling them to substitute primary and secondary methods leads to hijacking. I think we should consider shadowing with regards to the case of methods vs functions choices.
This does seem right to me, though. I think the writer generally knows what they are doing, so if they want to replace that method for their own use, that seems reasonable, even though it may go against what the author of the module with the type's definition wants.
I think that's okay.
But |
In reviewing #19306 and particularly On #19306, Michael suggested that we rely on this for compatibility functions, but that doesn't feel super-familiar to me in the method context, more in the standalone case. And even if we did rely on it for compatibility functions, it seems we could label the compatibility case as "last resort" so that it wouldn't trigger the ambiguity if the type provided the method directly? I realize this is "new" and not something we've discussed before (that I can recall). I think it reflects (a) new viewpoints on my part after the recent primary/secondary/tertiary methods visibility decisions (i.e., the decision that they are visible through their types, not names) and (b) being surprised by that breakList.chpl case's behavior on #19306. |
Responding to @bradcray -
Right I was not trying to say anything specific about methods specifically - just to say that we seem to have a pattern with "
I can get behind an ambiguity error for Case 1 and Case 3 (for the philosophical reasons you describe above and also because of the hijacking scenarios I outlined). It sounds like your viewpoint is that Case 2 should also be an ambiguity error. Am I understanding correctly? |
--- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
Finally found my way back here...
Case 2 is interesting. I don't currently feel strongly about it. If we decided that local scope methods shadowed other methods brought in by use/import, I'd be OK with that I think. OTOH, if we made it an ambiguity, we could change our minds later without that being a breaking change. |
In looking at an implementation of this in #19306 (comment) I am observing that simply making |
This is effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
Besides the above about overload sets, the only other thing I learned by implementing the strategy of completely ignoring method visibility is that we needed to change some operator methods into non-method operators to keep tests working as intended. (Because non-method operators can be "more visible"). |
(The above comment is interesting because operators can be both method-based and not method-based, so we have to define how one of each interacts for the purposes of shadowing). |
--- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
This is effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
I think you and I have expressed before that we've been surprised that we didn't trigger overload sets more often since enabling them. Are these new cases ones that feel like "Oh, yeah, of course that should be an overload set error, so this is better", or more like cases that feel unfortunate or like a step backwards in some way?
Are these tests that were specifically written to see how the shadowing behaves for operators, or are they cases that resemble real user codes and would want/need to make this sort of change to continue working? |
I'm not really sure. https://github.com/chapel-lang/chapel/blob/main/test/functions/operatorOverloads/operatorMethods/allOps/ioOperator.chpl is an example that would give an overload set error if the overload sets don't have an idea of a "closer" method. But that specific case could be written as a non-method, too.
Usually they are tests of operators. An example is df1fe36. |
Re the breakList test (see #19352 (comment) ) and the adjustment discussed above to make disambiguation ignore method shadowing but overload set checking be aware of "closer" (#19352 (comment)) -- I am finding that the Why? First, disambiguation prefers the new overload Second, overload checking correctly considers the new overload "more visible" - this pattern is summed up here: use LinkedLists;
proc bar() {
use LibGL; // defines a tertiary method LinkedList.append
var li = new LinkedList(real);
li.append(3); // uses LibGL.LinkedList.append because the 'use' had closer scope
} As a result there is no overload sets error. I am currently thinking that
is OK for PR #19306. However, we have to make some decision - if "more visible" is ignored during disambiguation for methods, should it also be ignored for overload set checking? (and if it is, how can we fix |
Is this a typo? It seems like |
I don't think this changes my position as stated in #19352 (comment) or the one I gave when talking about operators:
|
It's not a typo. The LinkedList has So, the Meanwhile, use LinkedLists;
proc bar() {
use LibGL; // defines a tertiary method proc LinkedList.append(x: int)
var li = new LinkedList(real);
li.append(3); // uses LibGL.LinkedList.append because the 'use' had closer scope
}
|
Are you saying that, from the OP, Case 1 and Case 3 should be ambiguity errors but Case 2 should be allowed (and prefer the tertiary method)? Or something else? From the comment you linked -
I do not know what this means. Are you trying to make a new proposal? Something other than "ambiguity" in Case 1 and Case 3 and the current behavior in Case 2? If you are making a new proposal, could you write an example to show what it is you are proposing? |
--- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
This is effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
More to do with deciding about overload set checking and method visibility, but this behavior makes sense with what is implemented now. See chapel-lang#19352 (comment) --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
Offline Michael and I have discussed a bit about Case 2, which I think should be prohibited by issuing an ambiguity error. A specific case where I have exploited we don't do that before was to redefine array accessors using tertiary methods to log different kinds of array accesses. I did that in the context of a unit test. However, I think that's too much power for the user to have. And I am not convinced that this is a useful feature. Can this be used to change the behavior of an existing source by adding an innocent-looking module, for example? I think that can be the case, and that's a scary prospect. The following gives an ambiguity error for example: (The compiler says "ambiguous call" which is good enough. But it puts the blame on the call site, listing two candidates. I think the blame should be on the definition of the secondary method.) record R {
proc foo() {
writeln("primary");
}
}
proc R.foo() {
writeln("secondary");
}
var r = new R();
r.foo(); where a secondary method shadows a primary. I think this behavior is correct and similarly, we shouldn't allow tertiary methods to shadow primary/secondary ones. |
Ah, of course, thank you! Yeah, that makes sense.
I'm torn - I think the writers of a tertiary method should be informed when that method is not usable because there is a primary/secondary method. However, I don't want a side module to break interactions with the main module where the type is defined because that seems like hijacking. At least, though, in the case of an ambiguity the user of both will know that they need to be careful what they bring in. |
Well, at least making it an ambiguity should enable future language changes that make it compile and run, right? Meaning, making it an ambiguity is the design we should prefer if we have some uncertainty here? |
--- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
This is effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
More to do with deciding about overload set checking and method visibility, but this behavior makes sense with what is implemented now. See chapel-lang#19352 (comment) --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
--- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
This is effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
effectively Case 2 in chapel-lang#19352. --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
More to do with deciding about overload set checking and method visibility, but this behavior makes sense with what is implemented now. See chapel-lang#19352 (comment) --- Signed-off-by: Michael Ferguson <mppf@users.noreply.github.com>
simplify use/import shadowing This PR describes and implements some candidate language adjustments to shadowing behavior for use and import statements. We need to do something in this area because the definition of shadowing is currently inconsistent between variables and functions (#19167). This PR attempts to simplify the language design in this area. The adjustments to the language design in this PR are as follows: * isMoreVisible in function disambiguation as well as scope resolution use the same rules for determining shadowing with use/import statements * isMoreVisible starts it search from the POI where the candidates were discovered (see issue #19198 -- not discussed further here) * private use statements still have two shadow scopes * public and private import statements now do not introduce a shadow scope * public use statements now do not introduce a shadow scope * `public use M` does not bring in `M` (but `public use M as M` does) * methods are no longer subject to shadowing Note that the above design leads to less shadowing of symbols from the automatic library (see the section "Less Shadowing of Symbols from the Automatic Standard Library" in #19306 (comment)) ## Discussion Elements of the language design direction are discussed in these issues: * #19167 * #19160 * #19219 and #13925 * #19312 * #19352 Please see also #19306 (comment) which discusses pros and cons of these language design choices. ### Testing and Review Reviewed by @lydia-duncan - thanks! This PR passes full local futures testing. Resolves the future `test/modules/vass/use-depth.chpl`. Also fixes #19198 and adds a test based on the case in that issue. - [x] full local futures testing - [x] gasnet testing ### Future Work There are two areas of future work that we would like to address soon: * #19313 which relates to changes in this PR to the test `modules/bradc/userInsteadOfStandard/foo2`. The behavior for trying to replace a standard module has changed (presumably due to minor changes in how the usual standard modules are now available). I think that changing the compiler to separately list each standard module would bring the behavior back the way it was. (Or we could look for other implementation changes to support it). * #19780 to add warnings for cases where the difference between `public use M` and `private use M` might be surprising
I'm closing this issue because PR #19306 resolved the 4 cases - 1, 2a, 2b, and 3 all result in ambiguity after that PR and these are included as tests in test/modules/shadowing. |
For https://github.com/Cray/chapel-private/issues/2690
This issue is asking a question about how the rule to find visible functions from the method's definition point interacts with shadowing. #16415 summarizes the current design which was implemented in PR #16208. PR #16976 implemented a related feature where
use SomeModule.someType
orimport SomeModule.someType
will bring in methods defined onsomeType
from that module.For the most part, the current behavior seems to be similar to the type's definition point being a last place to look if nothing else is found. In other words, methods available locally or via a
use
statement will always shadow methods available only from the type's definition point. I do not view the current situation as an obvious combination of other ideas but it is something we could keep and then document.The remainder of this issue discusses a few specific examples. In these examples, we will assume this is the start of the program:
Case 1: Can Import of a Type Cause Shadowing?
First, even after PR #19306, the
import SomeModule.someType
does not seem to shadow at all. Arguably it should, but maybe an ambiguity error is bettor for hijacking scenarios.Case 2: Should Type's Definition Point be Shadowed?
How should methods brought in from the type's definition point interact with a method defined locally? Today the local method shadows the ones from the definition point.
Case 3: Should a Use Statement Cause Shadowing?
Should a method available via a
use
statement always shadow methods available only from the type's definition point? Arguably, it would be better for this to be an ambiguity error, to prevent a hijacking scenario.Interaction with Adding a Method / Breaking vs Non-Breaking and Hijacking
If
Library
didn't havemethod
butProgram
did have one, then whenLibrary
adds one it will be shadowed by theProgram
version (non-breaking). That seems OK.However, when
Program
depends on bothLibrary
andLibraryPlus
and these addmethod
in one order or another, I don't see how it is possible to keep that method addition as a non-breaking change toProgram
.If
Library
didn't havemethod
butLibraryPlus
added it, then whenLibrary
adds one it will currently be an ambiguity error (Case 1) (breaking) or will be shadowed by theLibraryPlus
version (Case 3) (non-breaking). If we changed it to be shadowed by theLibrary
version, it would be worse (breaking / hijacking).If
Library
hasmethod
butLibraryPlus
didn't, and thenLibraryPlus
adds one, it will currently be an ambiguity error (Case 1) (breaking) or it will will be shadowed by theLibraryPlus
version (Case 3) (breaking / hijacking).So, arguably, it would be better for Case 1 and Case 3 to be ambiguity errors, since if we prefer one or the other for shadowing, that introduces the possibility of surprising behavior changes when a method is added. At least the ambiguity errors would point out that there is a potential problem.
However, making these into ambiguity errors seems to make the way use/import find methods for a type very different from how they would find other symbols. I think the result would be counter-intuitive.
The text was updated successfully, but these errors were encountered: