-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Address a number of variance safety issues with code in interfaces. #39839
Conversation
@@ -56,7 +56,7 @@ public bool MustHaveDefaultConstructor | |||
|
|||
public TypeParameterVariance Variance | |||
{ | |||
get { return _parentParameter.Variance; } | |||
get { return _inheritingType.IsInterface || _inheritingType.IsDelegate ? _parentParameter.Variance : TypeParameterVariance.NonVariant; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand this change. In what situation is the inheriting type not an interface or a delegate, but the parentParameter variance is variant, and that is incorrect? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand this change. In what situation is the inheriting type not an interface or a delegate, but the parentParameter variance is variant, and that is incorrect?
A class nested into an interface (for example, a display class) is inheriting type parameters of the containing interface. Those type parameters could be variant, but only interfaces and delegates are allowed to have variant type parameters.
In reply to: 347048798 [](ancestors = 347048798)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thanks. #Resolved
} | ||
} | ||
|
||
internal static NamedTypeSymbol GetEnclosingVariantInterface(Symbol member) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this return NamedTypeSymbol?
since null is an intended part of the return type? #WontFix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this return NamedTypeSymbol? since null is an intended part of the return type?
Nullable Reference Types feature hasn't been enabled in this file yet.
In reply to: 347049241 [](ancestors = 347049241)
@@ -52,10 +54,70 @@ internal static void CheckInterfaceVarianceSafety(this NamedTypeSymbol interface | |||
case SymbolKind.Event: | |||
CheckEventVarianceSafety((EventSymbol)member, diagnostics); | |||
break; | |||
case SymbolKind.NamedType: | |||
CheckNestedTypeVarianceSafety((NamedTypeSymbol)member, diagnostics); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intent here is to make any nested type inside a variant interface an error, right? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intent here is to make any nested type inside a variant interface an error, right?
Not any nested type, delegates and interfaces are Ok.
In reply to: 347049921 [](ancestors = 347049921)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This covers all the things I can think of:
-
Local functions that would be put directly in the containing class because they only capture this or can take only ref struct environments.
-
Attempted optimization to merge environments, thus re-parenting the local function method.
-
Sharing another lambda's display class, which should be fine already.
LGTM
@dotnet/roslyn-compiler Please review, need second sign-off. |
@gafter, @dotnet/roslyn-compiler Please review, need second sign-off. |
@@ -1735,6 +1735,7 @@ internal enum ErrorCode | |||
#endregion diagnostics introduced for C# 8.0 | |||
|
|||
ERR_InternalError = 8751, | |||
ERR_VarianceInterfaceNesting = 8752, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ERR_VarianceInterfaceNesting [](start = 8, length = 28)
I think this error code should be moved into the section of "diagnostics introduced for C# 8.0" #Closed
@@ -246,7 +246,12 @@ private void InlineThisOnlyEnvironments() | |||
RemoveEnv(); | |||
} | |||
} | |||
else | |||
// If we are in a variant interface, runtime might not consider the | |||
// method synthesized directly within the interface as viriant safe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
viriant [](start = 71, length = 7)
spelling #Closed
bool isStruct = true; | ||
|
||
// If we are in a variant interface, runtime might not consider the | ||
// method synthesized directly within the interface as viriant safe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
viriant [](start = 75, length = 7)
spelling #Closed
originalMethod.MethodKind == MethodKind.LambdaMethod && | ||
_analysis.MethodsConvertedToDelegates.Contains(originalMethod)) || | ||
// If we are in a variant interface, runtime might not consider the | ||
// method synthesized directly within the interface as viriant safe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
viriant [](start = 80, length = 7)
spelling #Closed
|
||
internal static NamedTypeSymbol GetEnclosingVariantInterface(Symbol member) | ||
{ | ||
var container = member.ContainingType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var [](start = 12, length = 3)
Consider writing
for (var container = member.ContainingType; container is object; container = container.ContainingType)
#Closed
@"M1 | ||
M2"); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be helpful to add a test that demonstrates that variance safety is enforced on private methods in an interface. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done reviewing (Iteration 2).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! (Iteration 3)
Fixes #39731.