Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proposal for using aliases allowing type parameters and additional target types #4452

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions proposals/csharp-10.0/using-alias-improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Using alias improvements

## Summary

Extend `using` aliases to allow generic type aliases and additional alias target types.

## Generic type aliases

Type aliases may include type parameters.
```C#
using MyList<T> = System.Collections.Generic.List<T>; // ok
```

Type parameters do not need to be referenced in the target.
```C#
using MyType1<T> = System.String; // ok
using MyType2<T, U> = T; // ok
```

Type parameter references in an alias target have the same restrictions as type parameter references in other generic contexts.
```C#
using MyType3<T, U> = T.U; // error: type or namespace 'T' not found
using MyType3<T, U> = T<U>; // error: type or namespace 'T<>' not found
```

The same name may be used for multiple aliases with distinct arity.
```C#
using MyDictionary = System.Collections.Generic.Dictionary<string, object>;
using MyDictionary<T> = System.Collections.Generic.Dictionary<string, T>; // ok
using MyDictionary<U> = System.Collections.Generic.Dictionary<string, U>; // error: 'MyDictionary<>' already defined
```

A partially bound type is not valid.
The `using` aliases below are both valid, and the use of `MyList<>` is valid because the expansion `List<>` is an unbound type, but the use of `MyDictionary<>` invalid because `Dictionary<string,>` is a partially bound type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding an example of a valid use of MyDictionary. This section is still starting with the words "A partially bound type is not valid.", which doesn't really address the confusion.

```C#
using MyList<T> = System.Collections.Generic.List<T>;
using MyDictionary<T> = System.Collections.Generic.Dictionary<string, T>;

_ = typeof(MyList<>); // ok: 'List<>'
_ = typeof(MyDictionary<>); // error: 'Dictionary<string,>' is not valid
```

### Constraints
If type parameter constraints cannot be declared explicitly, then constraints in the alias target are verified at the use site only.
```C#
using Option<T> = System.Nullable<T>; // ok
static void F<T>(Option<T> t) { ... } // error: 'T' must be value type in 'Nullable<T>'
```

However, without explicit constraints, the compiler will treat `T?` as `Nullable<T>` in an alias target which means nullable reference types cannot be used with type parameters in aliases.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we even permit T? at all? Or if we do, is this line even true? If we view these as macros, then T? will mean whatever T? would mean to the final expansion, not anything predefined like Nullable<T>. For example, using MyMap<T> = Dictionary<T, T?>; would cause different expansions for int and string, where one would be mapped to int? and one would be mapped to string?.


The alternative is to allow explicit type parameter constraints, perhaps using the same syntax as generic type and method constraint clauses.
```C#
using MaybeNull<T> = T? where T : class;
using Option<T> = System.Nullable<T> where T : struct;
```

Explicit constraints would also allow restricting the use of the target type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

```C#
using ValueTypeList<T> = List<T> where T : struct;
static void F<T>(ValueTypeList<T> list) { ... } // error: 'T' must be a value type
```

### Variance
Type parameter variance cannot be specified explicitly in the alias declaration, because any resulting variance constraints would not be enforced at the public boundary of the assembly.

Instead variance is implied by the alias target and verified at the use site only.
```C#
using MyEnumerable<T> = System.Collections.Generic.IEnumerable<T>;
MyEnumerable<object> e = new string[0]; // ok
```

## Additional alias target types

Alias targets may include primitive types, arrays, pointers, function pointers, tuples, and `?`.
```C#
using MyInt = int;
using MyArray = int[];
using Option<T> = T?;
```

Tuples may include element names.
```C#
using MyTuple = (int Id, string Name);
```

Alias targets may not reference aliases.
```C#
using MyType1 = System.Int32;
using MyType2 = System.Nullable<MyType1>; // error: type or namespace 'MyType1' not found
```

`unsafe` is required for aliases with pointers at the use site but not at the declaration.
```C#
using MyPointer = int*; // ok
static void F(MyPointer ptr) { ... } // error: pointers require unsafe context
```

## Syntax

```antlr
using_directive
: 'using' ('static')? name ';'
| 'using' identifier_token type_parameter_list? '=' (name | type) ';'
;
```

## Alternatives
`using` aliases are not first class types, and although this proposal extends the expressiveness of aliases, it does not address that fundamental limitation.

Should type aliases be represented with an alternative approach (such as "roles") where aliases are first class types in metadata and member signatures?

## See also

- https://github.com/dotnet/csharplang/issues/1239
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-07-13.md#generics-and-generic-type-parameters-in-aliases
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-28.md#proposal-support-generics-and-generic-type-parameters-in-aliases