Overloading elementary function

Hi Damian -

I haven't been following this thread closely but I'm jumping in to say something about the name resolution rule you are asking about.

In order to understand for myself if the problem you are worried about in your last post is present, I made a really tiny program to explore it. My experiment indicates that it's not a problem. Here is the tiny program:

proc foo(x: real(?w)) {
  writeln("in foo(real)");
}

proc foo(x: complex(?w)) {
  writeln("in foo(complex)");
  foo(x.re); // intention: it should call foo(real)
}

var x: complex(128);
foo(x); // it will call foo(complex)

This program behaves as expected with main (which will soon be 1.33). I wouldn't expect that this program has changed in behavior in any recent releases. (and it also behaves in this expected way with the new type and call resolver which is not yet in production).

There was indeed a change to the disambiguation rules in 1.28 which is described in https://chapel-lang.org/releaseNotes/1.27-1.28/01-language.pdf starting from slide 64. Note that 1.28 was the release that fixed some unfortunate situations where code you thought was working with real(32) exclusively would use real(64) for some operations. The details of function overload resolution aka "disambiguation" are documented in the language spec as well; if you are interested in looking at it, head to Procedures — Chapel Documentation 1.32 and more specifically to Procedures — Chapel Documentation 1.32 .

Now, what Brad was describing about the original program is accurate, but the "more visible" rule does not come up in my tiny program above because, just because we are within foo(complex), we do not consider foo(complex) to be more visible than foo(real). Instead, the visibility of both is the same (after all, they are both defined at the top-level scope).

What would make it "more visible" ? Well, as Brad already described, the things brought in by a use / private use are less visible than the things declared in a scope. Other than that, the visibility distance can be thought of as the number of code blocks (e.g. demarcated with { } although you can also have a code block with a do) between the call in question and the definitions. For example:

proc bar(x: real) {
  writeln("in bar(real)");
}

proc baz() {
  proc bar(x: complex) {
    writeln("in foo(complex)");
  }
  var num : real(64);
  bar(num); // this runs bar(complex) because it is closer / "more visible"
}

baz();

Admittedly, the language specification does not currently do a great job of defining "more visible" as a concept, and I think that's a good area for improvement to that document.

Also, one more thing about visibility. Using import instead of use changes the visibility because things brought in with import are considered siblings of the things defined in a scope. The reason is that, since import brings in just a named function / set of overloads, we think it's less likely to be surprising if it starts to bring in a new symbol. However, trying this with your sqrt example led to other problems: now you sqrt is ambiguous with the one brought in by import Math.sqrt; and additionally we get an overload sets error which is attempting to protect your code from changes in the libraries you are using.

In any case, I think it's very likely that your existing code will work fine in this regard if it worked with 1.28, and I suspect it did because that release fixed some implicit conversion issues that were troubling you. We are expecting the language rules in this area to be stable and I'm not aware of any upcoming changes to them (and, IIRC, 1.28 was the version where they last changed in any big way). And, if you use the strategy of defining groups of overloads next to each other (e.g. in your latest example you defined proc sqrt(x : real(?w)) next to proc sqrt(x : complex(?w))) things should be pretty satisfying. You also might consider trying to use different function names from Math/AutoMath in order to avoid confusion about which is called.

Tangentially related, I think you'd still like a compilation mode that disabled all implicit conversions, but we have issues for that and it's a longer-term TODO that is described in issue Compiler warnings about implicit type conversions involving floating point numbers · Issue #20687 · chapel-lang/chapel · GitHub . Of course, we are interested to know if that becomes a blocker / major issue for you.

Additionally, I think we'd consider adding warnings for cases that are both unlikely to come up in correct codes and likely to represent an error or confusion on the part of the programmer; so if you feel you have found such a case, do let us know about it.

Best,

-michael