From 7bf8ab86c31a061badb9b3b44c5eb05eb3ae3ed1 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 6 Nov 2020 12:03:54 -0800 Subject: [PATCH 1/5] Proposal: using alias improvements --- proposals/using-alias-improvements.md | 104 ++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 proposals/using-alias-improvements.md diff --git a/proposals/using-alias-improvements.md b/proposals/using-alias-improvements.md new file mode 100644 index 0000000000..54160f03c3 --- /dev/null +++ b/proposals/using-alias-improvements.md @@ -0,0 +1,104 @@ +# Using alias improvements + +## Summary + +Support extensions to `using` aliases: assembly-wide aliases; generic type aliases; and additional alias targets. + +## Assembly-wide aliases + +`using` aliases with a `global` modifier are assembly-wide aliases that apply to all compilation units in the assembly. + +Assembly-wide aliases may be declared at the root scope, not within a `namespace`. +```C# +global using MyInt = System.Int32; // ok +namespace MyNamespace +{ + global using MyString = System.String; // error: declared in 'MyNamespace' +} +``` +It is an error if there are multiple declarations of the same assembly-wide alias in the compilation, even if the declarations are identical. +Assembly-wide aliases can be hidden by names in the compilation unit, including local `using` aliases, although it is an error if the assembly-wide alias and local alias are declared in the same compilation unit. + +Aliases are not represented in metadata and are not available outside the assembly, not even in assemblies with `[InternalsVisibleTo]` access. + +## Generic type aliases + +Type aliases may include type parameters. +```C# +using MyList = System.Collections.Generic.List; //ok +``` + +Type parameter constraints cannot be specified explicitly. Any constraints from type parameters in the alias target must be satisfied at the use site. +```C# +using Option = System.Nullable; +static void F(Option t) { ... } // error: 'T' must be value type in 'Nullable' +``` + +Since constraints cannot be specified, use of `T?` is treated as `System.Nullable` if `T` is a type parameter in the alias. _This prevents use of nullable reference types for type parameters in aliases._ + +Type parameter variance cannot be specified explicitly. Variance is inferred from the alias target. +```C# +using MyEnumerable = IEnumerable; +static void F(MyEnumerable e) { ... }; +F(new string[0]); // ok +``` + +The target for a generic alias must be a type. _What other restrictions are there on the target?_ +```C# +using MyType1 = System.String; // ok? +using MyType2 = T; // ok? +using MyType3 = T; // ok? +``` + +The same name may be used for multiple aliases with distinct arity. +```C# +using MyDictionary = System.Collections.Generic.Dictionary; +using MyDictionary = System.Collections.Generic.Dictionary; // ok +using MyDictionary = System.Collections.Generic.Dictionary; // error: 'MyDictionary<>' already defined +``` + +A partially bound type is not valid. +```C# +using MyList = System.Collections.Generic.List; +using MyDictionary = System.Collections.Generic.Dictionary; +_ = typef(MyList<>); // ok: 'List<>' +_ = typeof(MyDictionary<>); // error: 'Dictionary' is not valid +``` + +## Additional alias targets + +Alias targets may include: primitive types, arrays, pointers, function pointers, `T?`, tuples, and other type aliases. +Tuples may include element names. +```C# +using MyInt = int; +using MyArray = int[]; +using Option = T?; +using MyTuple = (int Id, string Name); +``` + +`unsafe` is required at the use site but not at the declaration for aliases including pointers. +```C# +using MyPointer = int*; // ok +static void F(MyPointer ptr) { ... } // error: pointers require unsafe context +``` + +It is an error for a type alias target to depend on itself, directly or indirectly. +```C# +using MyType1 = List; // error: 'MyType1' cannot depend on 'MyType1' +using MyType2 = IEnumerable; +``` + +## Syntax + +```antlr +using_directive + : 'using' ('static')? name ';' + | ('global')? 'using' identifier_token type_parameter_list? '=' (name | type) ';' + ; +``` + +## Design meetings + +Previous proposals: +- https://github.com/dotnet/csharplang/issues/1239 +- https://github.com/dotnet/csharplang/discussions/1300 From fc2d2896f4fccac3a353d6d256d8d2fc555d68a2 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 19 Feb 2021 12:17:44 -0800 Subject: [PATCH 2/5] Remove global aliases --- proposals/using-alias-improvements.md | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/proposals/using-alias-improvements.md b/proposals/using-alias-improvements.md index 54160f03c3..5f62f8e8d0 100644 --- a/proposals/using-alias-improvements.md +++ b/proposals/using-alias-improvements.md @@ -2,24 +2,7 @@ ## Summary -Support extensions to `using` aliases: assembly-wide aliases; generic type aliases; and additional alias targets. - -## Assembly-wide aliases - -`using` aliases with a `global` modifier are assembly-wide aliases that apply to all compilation units in the assembly. - -Assembly-wide aliases may be declared at the root scope, not within a `namespace`. -```C# -global using MyInt = System.Int32; // ok -namespace MyNamespace -{ - global using MyString = System.String; // error: declared in 'MyNamespace' -} -``` -It is an error if there are multiple declarations of the same assembly-wide alias in the compilation, even if the declarations are identical. -Assembly-wide aliases can be hidden by names in the compilation unit, including local `using` aliases, although it is an error if the assembly-wide alias and local alias are declared in the same compilation unit. - -Aliases are not represented in metadata and are not available outside the assembly, not even in assemblies with `[InternalsVisibleTo]` access. +Extend `using` aliases to allow generic type aliases and additional alias target types. ## Generic type aliases @@ -93,7 +76,7 @@ using MyType2 = IEnumerable; ```antlr using_directive : 'using' ('static')? name ';' - | ('global')? 'using' identifier_token type_parameter_list? '=' (name | type) ';' + | 'using' identifier_token type_parameter_list? '=' (name | type) ';' ; ``` From f09096c04cbf3de7f1fa7ac4ed96938f728a0b94 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 19 Feb 2021 13:00:41 -0800 Subject: [PATCH 3/5] Move to proposals/csharp-10.0 --- proposals/{ => csharp-10.0}/using-alias-improvements.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{ => csharp-10.0}/using-alias-improvements.md (100%) diff --git a/proposals/using-alias-improvements.md b/proposals/csharp-10.0/using-alias-improvements.md similarity index 100% rename from proposals/using-alias-improvements.md rename to proposals/csharp-10.0/using-alias-improvements.md From 0c9e26b57b8437b2c7a1ef6959f87e3dbc393088 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 19 Feb 2021 08:26:47 -0800 Subject: [PATCH 4/5] Updates and PR feedback --- .../csharp-10.0/using-alias-improvements.md | 81 ++++++++++++------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/proposals/csharp-10.0/using-alias-improvements.md b/proposals/csharp-10.0/using-alias-improvements.md index 5f62f8e8d0..cbb8cc42f8 100644 --- a/proposals/csharp-10.0/using-alias-improvements.md +++ b/proposals/csharp-10.0/using-alias-improvements.md @@ -8,29 +8,19 @@ Extend `using` aliases to allow generic type aliases and additional alias target Type aliases may include type parameters. ```C# -using MyList = System.Collections.Generic.List; //ok +using MyList = System.Collections.Generic.List; // ok ``` -Type parameter constraints cannot be specified explicitly. Any constraints from type parameters in the alias target must be satisfied at the use site. +Type parameters do not need to be referenced in the target. ```C# -using Option = System.Nullable; -static void F(Option t) { ... } // error: 'T' must be value type in 'Nullable' -``` - -Since constraints cannot be specified, use of `T?` is treated as `System.Nullable` if `T` is a type parameter in the alias. _This prevents use of nullable reference types for type parameters in aliases._ - -Type parameter variance cannot be specified explicitly. Variance is inferred from the alias target. -```C# -using MyEnumerable = IEnumerable; -static void F(MyEnumerable e) { ... }; -F(new string[0]); // ok +using MyType1 = System.String; // ok +using MyType2 = T; // ok ``` -The target for a generic alias must be a type. _What other restrictions are there on the target?_ +Type parameter references in an alias target have the same restrictions as type parameter references in other generic contexts. ```C# -using MyType1 = System.String; // ok? -using MyType2 = T; // ok? -using MyType3 = T; // ok? +using MyType3 = T.U; // error: reported at use site only? +using MyType3 = T; // error: reported at use site only? ``` The same name may be used for multiple aliases with distinct arity. @@ -44,31 +34,59 @@ A partially bound type is not valid. ```C# using MyList = System.Collections.Generic.List; using MyDictionary = System.Collections.Generic.Dictionary; + _ = typef(MyList<>); // ok: 'List<>' _ = typeof(MyDictionary<>); // error: 'Dictionary' is not valid ``` -## Additional alias targets +### 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 = System.Nullable; // ok +static void F(Option t) { ... } // error: 'T' must be value type in 'Nullable' +``` -Alias targets may include: primitive types, arrays, pointers, function pointers, `T?`, tuples, and other type aliases. -Tuples may include element names. +However, without explicit constraints, the compiler will treat `T?` as `Nullable` in an alias target which means nullable reference types cannot be used with type parameters in aliases. + +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? where T : class; +using Option = System.Nullable where T : struct; +``` + +### 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 = System.Collections.Generic.IEnumerable; +MyEnumerable 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?; +``` + +Tuples may include element names. +```C# using MyTuple = (int Id, string Name); ``` -`unsafe` is required at the use site but not at the declaration for aliases including pointers. +Alias targets may not reference aliases. ```C# -using MyPointer = int*; // ok -static void F(MyPointer ptr) { ... } // error: pointers require unsafe context +using MyType1 = System.Int32; +using MyType2 = System.Nullable; // error: type or namespace 'MyType1' not found ``` -It is an error for a type alias target to depend on itself, directly or indirectly. +`unsafe` is required for aliases with pointers at the use site but not at the declaration. ```C# -using MyType1 = List; // error: 'MyType1' cannot depend on 'MyType1' -using MyType2 = IEnumerable; +using MyPointer = int*; // ok +static void F(MyPointer ptr) { ... } // error: pointers require unsafe context ``` ## Syntax @@ -80,8 +98,13 @@ using_directive ; ``` -## Design meetings +## 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 -Previous proposals: - https://github.com/dotnet/csharplang/issues/1239 -- https://github.com/dotnet/csharplang/discussions/1300 +- 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 From e7d050899b0858c3ff89160e22466f15cda5501e Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Sun, 21 Feb 2021 16:44:05 -0800 Subject: [PATCH 5/5] Updates --- proposals/csharp-10.0/using-alias-improvements.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/proposals/csharp-10.0/using-alias-improvements.md b/proposals/csharp-10.0/using-alias-improvements.md index cbb8cc42f8..5f3e00a73d 100644 --- a/proposals/csharp-10.0/using-alias-improvements.md +++ b/proposals/csharp-10.0/using-alias-improvements.md @@ -19,8 +19,8 @@ using MyType2 = 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; // error: reported at use site only? -using MyType3 = T; // error: reported at use site only? +using MyType3 = T.U; // error: type or namespace 'T' not found +using MyType3 = T; // error: type or namespace 'T<>' not found ``` The same name may be used for multiple aliases with distinct arity. @@ -31,11 +31,12 @@ using MyDictionary = System.Collections.Generic.Dictionary; // err ``` 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` is a partially bound type. ```C# using MyList = System.Collections.Generic.List; using MyDictionary = System.Collections.Generic.Dictionary; -_ = typef(MyList<>); // ok: 'List<>' +_ = typeof(MyList<>); // ok: 'List<>' _ = typeof(MyDictionary<>); // error: 'Dictionary' is not valid ``` @@ -54,6 +55,12 @@ using MaybeNull = T? where T : class; using Option = System.Nullable where T : struct; ``` +Explicit constraints would also allow restricting the use of the target type. +```C# +using ValueTypeList = List where T : struct; +static void F(ValueTypeList 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.