An AI on visibility needed

Consider the following:

   type S is (A);
   A : Integer; -- Illegal, literal A conflicts with object A

OK, that is an insane rule in itself, but let it be.

What about this

   type T is (A);
   declare
      type S is new T; -- Implicitly introduce A
      A : Integer;     -- This is OK, Integer A kills, not overloads, A of S

Now it becomes bipolar. Why the former is OK and the latter is not? How do you reach A of S (without prior renaming)? Or A of T.

I think visibility rules as they are make no sense and need to be fixed one way or another.

The Ada 83 Rationale, specifically Chapter 11, goes into depth over why they made certain decisions regarding overloading and visibility. Neither of those rules seem insane to me.

Well sane decisions may lead to insane results. But I see nothing in the Rationale why a directly visible function (enumeration literal is a function) must be hidden by an object in one case and overloaded in another.

P.S. There is a another case producing similar illogical result, but it is related to the use-clause. It is worse than this one, because it is a booby trap that makes a legal program illegal when the specification of a package in the use-clause gets changed to its semantic equivalent:

function Immutable return Integer;

to

Immutable : constant Integer; -- Deferred constant

Note that this directly contradicts the Rationale:

Another consequence is that a name that is made directly visible by a use clause cannot hide another name.

It can and it does! If one name is an object and another name is a function, literal, package whatever, then the former kills itself and its opponent.

In the first example, A the enum literal and A the object are defined in the same declarative region. In Ada the rule is that if you declare an object (meaning a variable or constant) in a declarative region, then no other entity can be declared with the same name in that declarative region. Of course nested scopes can hide the name. (kill seems like an excessively violent term for this)

In the second example, A the enum literal is introduced implicitly via the derived type declaration; it is not explicitly declared in the region. I suspect the rationale for allowing overloading here is to avoid the case where adding a new enum literal to the base type causes compile errors at all places that derive from that type and also have existing entities with the same name. Your third example seems to be a case where that principle fails, though.

Derived types in general lead to a lot of confusing edge cases and complex rules. But there is no way to simplify any of this in a language proposal because it would break tons of existing code going back to Ada 83.

Why should it cause errors? The names refer to different types.

There are three different behaviours of same entities:

  • Syntax. Explicitly declared literal and object make program illegal
  • Hiding. Object hides the literal declared per derivation
  • Seppuku. Object and literal from two use-clauses make the name unusable

Kills refer to the tird case.

Why should it cause errors? The names refer to different types.

It would violate the rule stating no other entity shall be visible and have the same name as a variable/constant that is currently visible.

The Rationale mentions that this is a rule they wanted to follow.

I mistakenly wrote overloading here. (hiding, not overloading, is what happens in that case)

(Everything except literals and subroutines fall into this category)

And that is the point.

The rule could rather be:

  • A non-overloadable entity can hide only other non-overloadable entity and only in a nested context.
  • No use clause can hide anything.
  • No overloadable entity can be hidden (except for declaring it abstract).