New Issue: Visibility rules for methods

16415, “vasslitvinov”, “Visibility rules for methods”, “2020-09-18T06:00:51Z”

When is the method Bar.doSomething() visible from a call site?

Basic cases

(Rule A - lexical scoping) doSomething() is defined in the same or lexically-enclosing scope. Can be public or private. This is the same as for non-methods. Status: agreed on.

(Rule B - unrestricted use) doSomething() is public or re-exported by a module whose use is visible from the call site. This is the same as for non-methods. Status: agreed on.

(Rule C - visible from Bar definition) doSomething() is public and visible from the definition of Bar. For example, it is a primary method on Bar or a secondary method defined in the same scope as Bar. Status: agreed on.

(Rule D - always) doSomething() is defined as a method on Bar anywhere in the program. Option: must be public. Example: UserOfBar1 here. Status: open - requested by Brad, opposed by Vass.

(Rule E - visible from producer) doSomething() is public and visible from the definition of the function that produced the Bar receiver, ex. (producer()).doSomething()(). Status: no supporters.

Third-party definitions

How to bring in a method from a “third-party” module, i.e. other than where Bar is defined?

We are looking to choose some of the options below. This is a summary of the discussion in #16357. Reasoning is partly motivated by Brad’s analogy between a method and a symbol within a module here. We start with Michael’s example:

module DefinesBar {
  record Bar { }
}

module DefinesBarMethod {
  use DefinesBar;
  proc Bar.doSomething() { }
}

module ReturnsBar {
  proc makeBar() {
    use DefinesBar;
    return new Bar();
  }
}

module UserOfBar2 {
  import ReturnsBar;
  var x = ReturnsBar.makeBar();

  // How to allow 'x.doSomething()' below to compile?
  // Options:
  use DefinesBarMethod;                      // (Rule B - unrestricted use)
  use DefinesBarMethod only;                 // (Rule F - empty only)
  use DefinesBarMethod only Bar;             // (Rule G - type brings its methods)
  use DefinesBarMethod only doSomething;     // (Rule H - symbols include methods)
  use DefinesBarMethod only Bar.*;           // (Rule I - dot star)
  use DefinesBarMethod only Bar.doSomething; // (Rule J - dot name)

  x.doSomething();
}

Illustrated in the code above:

(Rule F - empty only) use DefinesBarMethod only makes doSomething() visible.
Status: generally opposed.

(Rule G - type brings its methods) only Bar makes all methods on Bar also visible.
Status: this is a key feature under discussion.

(Rule H - symbols include methods) only doSomething applies to all symbols including methods.
Status: mostly opposed.

(Rule I - dot star) Bar.* specifies all methods of the type Bar. Option: includes the type Bar itself.
Status: replacement or (future) addition to Rule G.

(Rule J - dot name) Bar.doSomething specifies methods with the specific name.
Status: (future) addition.

Additionally:

(Rule P - applies only to public) Rules (G)…(J) additionally require that doSomething() be public. Private methods cannot be accessed outside their module.
Status: generally agreed on.

(Rule R - requires re-export) Rules (G)…(J) additionally require that Bar be re-exported by the third-party module DefinesBarMethod.
Status: uncertain.

While above we discuss only the only clause, the same rules apply to import and (with the opposite meaning) to except.

Other

The POI rule and dynamic dispatch / virtual methods are not discussed here.

Special methods – the visibility rules are to be discussed separately for:
init
init=
deinit
= (assignment)
op=

How to handle punctuation-based operators like + ?