New Issue: should it be possible to define a module that shadows an automatically included symbol?

19312, "mppf", "should it be possible to define a module that shadows an automatically included symbol?", "2022-02-25T15:19:01Z"

This is a spin-off from definition of shadowing is inconsistent between variables and functions · Issue #19167 · chapel-lang/chapel · GitHub

The automatic modules are implicitly used at the start of each module. Should it be possible to create a module that shadows something from these automatic modules?

Here is an example (Note that today, Math is an automatic module and defines param e):

module ReplaceE { param e = 10; }
module Program {
  // use AutomaticModules / use Math; is effectively here but implicit
  use ReplaceE;
  e; // should this refer to ReplaceE.e or be ambiguous?
}

I think that the main argument that I know of in favor of allowing shadowing here is that we would like to be able to add automatically-included functions and variables without breaking programs.

Here is a story about how that could come up. Suppose we have this:

module AutomaticModules { }
module DefinesFoo { var foo = 10; }
module Program {
  // use AutomaticModules; is effectively here but implicit
  use DefinesFoo;
  foo; // OK, refers to DefinesFoo.foo
}

Now suppose that in StandardLibrary, we add a const foo:

module AutomaticModules { var foo = 20; }
module DefinesFoo { var foo = 10; }
module Program {
  // use AutomaticModules; is effectively here but implicit
  use DefinesFoo;
  foo; // Is this now an ambiguity?
}

Do we want the above program to continue to compile? I would suppose so - because I'd think that adding a top-level variable to the automatic modules should be a non-breaking change.

I think another viewpoint on this issue is - suppose everybody were using import, what would happen? It would get the user's foo and not give an ambiguity error. Here is an example:

module AutomaticModules { ... }
module DefinesFoo { var foo = 10; }
module Program {
  import AutomaticModules;
  import DefinesFoo.foo;
  foo; // always refers to DefinesFoo.foo even if AutomaticModules adds a foo
}

So, if we arrange for foo to always refer to DefinesFoo.foo even in the use statement case above, we are making the situation more consistent with what we would regard as the good behavior from import.

I think the main alternative to this strategy is to avoid adding symbols to the set of automatically-included symbols and to consider it a breaking change when we do.