AdaCore / ada-spark-rfcs

Platform to submit RFCs for the Ada & SPARK languages
62 stars 28 forks source link

Name-specific use clause #90

Open danhettena-nvidia opened 2 years ago

danhettena-nvidia commented 2 years ago

Summary

Analogous to C++ using-declarations, and analogous to the "from" form of Python import statements, add a fourth kind of Ada use clause that uses a name from a package. For example:

    procedure Example is
        package A is
            function Foo(x : Integer) return Integer;
            function Foo(x : Float) return Float;
        end A;
        package body A is
            function Foo(x : Integer) return Integer is begin return x; end Foo;
            function Foo(x : Float) return Float is begin return x; end Foo;
        end A;
        -- New syntax:
        use A.Foo;
        -- Existing syntax:
        -- function Foo(x : Integer) return Integer renames A.Foo;
        -- function Foo(x : Float) return Float renames A.Foo;
        Result1 : Integer;
        Result2 : Float;
    begin
        Result1 := Foo(0);
        Pragma Unused(Result1);
        Result2 := Foo(0.0);
        Pragma Unused(Result2);
    end Example;

Motivation

If at some place within the scope of a _useclause, a naming conflict exists between a declaration in that scope and a declaration made potentially use-visible at that place by that _useclause, references to the identifier of the declarations may result in confusion as to which declaration is referenced through that identifier.

To mitigate this potential confusion, some critical software is compiled with configuration options that will cause compilation errors wherever such naming conflicts exist, or at least in some cases where such naming conflicts exist. For example, this effect can be achieved with the GNAT toolchain by combining the -gnatwh and -gnatwe compiler switches.

However, this approach threatens forward compatibility of the software that includes the _useclause. A naming conflict might not exist as of when the software with the _useclause is developed, but a subsequent addition to the package being used might introduce such a naming conflict. Such a naming conflict would have the undesired effect of causing compilation of the software with the _useclause to fail with errors.

Consequently, it is arguably not appropriate for critical software being developed at scale to include _use_packageclauses and use all type clauses. (The same concerns do not apply to use type clauses, however, since these only make primitive operators potentially use-visible, and primitive operators are syntactically limited in ways that mitigate the risk of confusion.)

An alternative approach is for critical software to utilize package renaming declarations to define shorthand. However, for frequently used declarations within another package, package renaming declarations are syntactically suboptimal.

As shown in the example above, optimal syntax for each reference to a subprogram declaration in another package can be accomplished with subprogram renaming, but subprogram renaming is even more cumbersome to use than package renaming, given the need to reiterate subprogram parameters and return types and to cover each overloaded declaration.

The ability to use a specific name from a package would address the above concerns elegantly and in a manner consistent with other modular programming languages that prioritize "safe" importing of specific content from other modules.

yannickmoy commented 2 years ago

I think this extension of use_clause makes perfect sense. Let's see what others think.

mosteo commented 2 years ago

I see a difference in that the rename brings a specific profile into view, whereas the new use would make all of them visible. Could this cause the same name conflicts as a regular use down the line?

briot commented 2 years ago

I had proposed a similar extension to use clauses a few years ago.

The idea was to simplify the use of generic instances. The use case is the following: say I start with a package containing a number of types and subprograms. Then it appears this package should actually be implemented via a generic because we need a similar package somewhere else.

So we had:

package P is       procedure Proc (V : Integer);       procedure Another_Proc (V : Integer);       procedure Specific_To_Integer (V : Integer);   end P;

And now we have

   generic        type T is private;    package G is       procedure Proc (V : T);       procedure Another_Proc (V : T);    end G;

   package P is       package Inst is new G (Integer);       procedure Proc (V : Integer) renames Inst.Proc;       procedure Another_Proc (V : Integer) renames Inst.Another_Proc;       procedure Specific_To_Integer (V : Integer);    end P;

But all these renames are a pain to write and maintain, it would be nicer to have some variant of use clause to bring all the definitions as if they had been declared in the current scope.

This came when I was working on a traits containers library.

I wonder whether this can be combined with the origin post in this thread.

Emmanuel

yannickmoy commented 2 years ago

I see a difference in that the rename brings a specific profile into view, whereas the new use would make all of them visible. Could this cause the same name conflicts as a regular use down the line?

Not more than multiple variable/type/subprogram/package renamings, that the new use_clause would replace. In general, it will be used for subprograms, which are less likely to cause name conflicts as they are overloadable (and you can in general solve the conflict with either type qualification of the parameters/result or full name qualification of the callee).

yannickmoy commented 2 years ago

@briot This is a different feature IMO, as you want to re-declare entities in a new scope, so that they can be visible from this new scope, while the intent here is simply to make entities visible in the new scope, which is the original semantics of use_clauses. Your proposed feature would be rather an extension of renamings such as:

procedure <> renames Inst.<>;

(to use the syntax of Ada for placeholders)