-
Notifications
You must be signed in to change notification settings - Fork 413
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
Should Chapel consider formal constness when disambiguating calls? #22809
Comments
@DanilaFe @jabraham17 Both pointed out that today, the spec takes a clear stance on not disambiguating overloads based on formal argument intents. In which case, I think it would be good if the example program was an ambiguity error, instead of preferring the |
I believe this is a bug; the spec (and the overload selection mechanism in production) says these functions are of equal specificity. The conditions for the I believe the bug is somewhere around lines 4356 and 4503 in In particular, a |
Not so sure why this case works when the other does not, but I don't think it's due to disambiguation itself. The return intent overloading is a later process & I think that's working differently here.
Agreed, and I agree with @DanilaFe in that I think this issue is a bug. In particular, I think it's specific to the case when the result of
I'm open to this, but it would be a language change, rather than an implementation one, because it will impact what the spec describes in the function resolution section. I know that historically we argued that the intents are irrelevant for candidate and overload selection; however that's not true -- they are relevant in various ways (e.g. only a |
Right, I think this should be a compiler error under the current language rules for one reason or another. Assigning to a proc main() {
var r = new rec();
const ref rx = r;
const ref f = foo(rx); // Now we have a target, we can disambiguate the RI? Nope...
} I'm not entirely convinced that this should work either. It seems super subtle that two functions that differ only in formal intents can be differentiated solely by RIO. In the case where no context is available for the call, it should definitely be an ambiguity error. But IMO, it should also be an ambiguity error even if there is context available. Or perhaps a new class of error or something.
That's correct, I'm advocating for a language change here. I understand that between value, ref, const ref overloads, generic instantiation, implicit conversion the candidate selection rules can get super subtle and confusing. So my hope is that maybe we can just keep this about |
@bradcray - I ran into this issue recently. This issue is proposing a language change and I'm not sure it's received attention. Do you think we should consider the language change here? That is, making something that is some sort of As it is now, it's a candidate but you would get a const-checking error if the candidate is selected. |
This part seems reasonable proposal to me; the obvious hesitation is what it implies for 2.0. However, it seems to me that it would effectively be useless for cases like the OP if we didn't also have a rule saying something like a |
I'd expect the return intent overloading to cover this case (as it does today), and so I'd limit the proposal to simply discarding candidates with |
I'm not seeing how return intent overloading comes into play, so am either missing something or not being clear. Trying to clarify in case it's the second, my interpretation of your previous comment is that in a situation like: proc foo(ref x: int) { writeln("In ref foo()"); }
proc foo(const ref x: int) { writeln("In const ref foo()"); } calls like: param answer = 42;
const answerToo = answer;
const ref answerRef = answer;
foo(42);
foo(answer);
foo(answerToo);
foo(answerRef); will all go to the second overload since the first isn't a candidate (since the actual can't be modified). But wouldn't calls like the following: var answer = 42;
ref answerRef = answer;
foo(answer);
foo(answerToo); result in ambiguity warnings since they could dispatch to either overload, unless we had a "prefers ref" rule? Or are you saying that return intent overloading has some sort of "prefers ref arguments" rule as part of it already? [I can see that if the call also involved return intent overloading and the callsites selected between those options then things might happen to work out. But in cases like the OP where the return value is dropped on the floor, would they? And/or are you suggesting that we'd want to say that the only way to do this sort of argument intent overloading is if there also happens to be return intent overloading?] |
Right
Right, they would be ambiguous if return intent overloading is not in play
Nope
Yes, this is what I'm arguing, partly from a viewpoint of not wanting to change more than we have to. The example in the OP had return intent overloading as well: proc foo(const ref r: rec) const ref { return r.x; }
proc foo(ref r: rec) ref { return r.x; }
FWIW, I think it's a bug to pick the But, to be clear, I think to meet what @dlongnecke-cray has been asking for here, yes, we would need a disambiguation change. I just don't think we should go that far at this point with 2.0 since IMO it completely replaces return intent overloading with something else that might have unknown problems when applied to our standard library. |
I definitely feel more nervous about relying on return intent overloading to make In any case, I agree that |
It sounds like we have some agreement on a starting point. However, I'd like to dig a little deeper into your concern and whether or not it's possible to address it. Suppose we did something to address your concern in the previous comment (where I think maybe you meant In other words, how/why can we make proc foo(ref x: int) { writeln("In ref foo()"); }
proc foo(const ref x: int) { writeln("In const ref foo()"); }
var x: int;
foo(x); // want it to choose the `ref` version rather than be ambiguous? not result in ambiguity without disrupting the similar return intent overloading case: proc foo(const ref r: rec) const ref { return r.x; }
proc foo(ref r: rec) ref { return r.x; }
var x = ...;
foo(x) = 4; // chooses ref overload
writeln(foo(x)); // chooses const ref overload ? The idea of adjusting the disambiguation rules to prefer a |
Hmm, that's a good point. I don't know if I believe the following, but just to wrestle with it before giving up:
So maybe the right thing to do in such a case (at least in the long-term) is to warn when we have an overload like mine in which one of the options will never be preferred? (i.e., "warning |
I've created a draft PR that makes the simple change under discussion here over at PR #24097. |
I ran into an interesting related case this morning while adding some testing for some operations on // A simpler program intended to simulate a problem
// facing complex.
proc ref int.foo() ref {
return this;
}
proc int.foo() {
return this;
}
proc main() {
1.foo(); // error: const actual is passed to 'ref' formal 'this' of foo()
} We can view this program as either:
|
If I'm understanding correctly, this seems sufficiently similar to me that it'd be nice to address here. Specifically, I'm thinking that for the direction we're pursuing for normal arguments, the first wouldn't be a candidate (because |
@bradcray - it's certainly related, and yes, PR #24097 should fix the issue in the new code sample. OTOH, I think we could fix it in a different way if we chose not to proceed with PR #24097. In particular, I expect that it's possible to fix return-intent overloading in this case to behave better. Point being, I don't think that the new example helps much in making a decision about PR #24097. Either way, I think we should include it in our efforts to improve the situation. |
Here is another reproducer for a problem that I observed when working with proc ref int.foo() ref {
return this;
}
proc int.foo() {
return this;
}
proc identity(arg) {
return arg;
}
proc main() {
var v = 1;
identity(v).foo(); // error: non-lvalue actual is passed to 'ref' formal 'this' of foo()
} The issue is also present when using a record: record R { var x: int; }
proc ref R.foo() ref {
return this;
}
proc R.foo() {
return this;
}
proc identity(arg) {
return arg;
}
proc main() {
var v = new R(1);
identity(v).foo(); // error: non-lvalue actual is passed to 'ref' formal 'this' of foo()
} As with the other new example, these could be solved by the proposal in PR #24097 but there are presumably other paths to solving it as well. |
For issue #24119. Resolves #24125. This PR adds overloads to AutoMath to implement `param` versions of: * `abs` for all numeric types * `sqrt` for floating-point types. Additionally, while testing, I realized that `complex.re` and `complex.im` did not have `param` returning versions, so this PR also adds such param-returning versions to ChapelBase. To implement these, I: * added `PRIM_SQRT` and `PRIM_ABS`. For now, these are just used for computing these functions on `param`s and they will cause the compiler to error out one makes it to code-generation time. * implemented folding for `PRIM_ABS`, `PRIM_SQRT`, `PRIM_GET_REAL`, and `PRIM_GET_IMAG` in num.cpp * enabled `postFoldPrimop` to fold `PRIM_ABS`, `PRIM_SQRT`, `PRIM_GET_REAL`, and `PRIM_GET_IMAG` This PR also updates the spec description of `complex` to make it clearer. While there, it removes `proc complex.bits` from the spec, since this method does not exist other than in the spec. Reviewed by @DanilaFe - thanks! - [x] full comm=none testing Future Work: * #22809 * in particular, resolve two confusing errors I posted as comments based on work on this PR * update the dyno resolver to assert `PRIM_ABS`, `PRIM_SQRT` are only used on `param` values * update the dyno resolver to implement `PRIM_GET_REAL` and `PRIM_GET_IMAG` on `param` values
Or we use it as motivation or fodder for a future Chapel 3.0 release. |
Why would the user want to write an overload that takes a [Added] Thinking about #25343, for the purposes of this discussion we will treat formals of the type |
Should this program compile? If so, what overload of
foo
shouldbe selected?
Today this program is a compilation error. The compiler selects the
ref
overload offoo
and then complains about passingrx
to a
ref
formal.As part of an outcome of the
Reflection
module review, I've beentrying to unify
getField
andgetFieldRef
, where the secondprocedure only differs from the first in that it takes the first
formal by
ref
and returns byref
.I was surprised to see the compiler prefer the
ref
overload always,and wrote another sample program (given above), in which the same
behavior occurs.
To me, it seems intuitive that the program should compile and the
const ref
overload offoo
should be selected.Implementation-wise, I think the compiler should not even consider
the
ref
overload offoo
as a candidate for this call, on thebasis that the actual is marked
const ref
.Note that a limited variation of this disambiguation already occurs
today for method calls:
But outside of receivers in method calls, the spec makes no mention
of constness when selecting candidates for a call:
https://chapel-lang.org/docs/language/spec/procedures.html?highlight=call%20disambiguation#function-resolution
The text was updated successfully, but these errors were encountered: