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

Address a number of variance safety issues with code in interfaces. #39839

Merged
merged 3 commits into from
Nov 20, 2019

Conversation

AlekseyTs
Copy link
Contributor

@AlekseyTs AlekseyTs commented Nov 15, 2019

Fixes #39731.

  • Declaration of classes, structures and enums within variant interfaces is disallowed.
  • Nested classes synthesized by the compiler don't have variant emitted type parameters.
  • Local functions and lambdas used within a variant interface always have display class.

@AlekseyTs AlekseyTs requested review from agocke and a team November 15, 2019 20:46
@@ -56,7 +56,7 @@ public bool MustHaveDefaultConstructor

public TypeParameterVariance Variance
{
get { return _parentParameter.Variance; }
get { return _inheritingType.IsInterface || _inheritingType.IsDelegate ? _parentParameter.Variance : TypeParameterVariance.NonVariant; }
Copy link
Member

@agocke agocke Nov 15, 2019

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

Copy link
Contributor Author

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)

Copy link
Member

@agocke agocke Nov 16, 2019

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)
Copy link
Member

@agocke agocke Nov 15, 2019

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

Copy link
Contributor Author

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);
Copy link
Member

@agocke agocke Nov 15, 2019

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

Copy link
Contributor Author

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)

Copy link
Member

@agocke agocke left a 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:

  1. Local functions that would be put directly in the containing class because they only capture this or can take only ref struct environments.

  2. Attempted optimization to merge environments, thus re-parenting the local function method.

  3. Sharing another lambda's display class, which should be fine already.

LGTM

@AlekseyTs
Copy link
Contributor Author

@dotnet/roslyn-compiler Please review, need second sign-off.

@AlekseyTs
Copy link
Contributor Author

@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,
Copy link
Member

@gafter gafter Nov 19, 2019

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.
Copy link
Member

@gafter gafter Nov 19, 2019

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.
Copy link
Member

@gafter gafter Nov 19, 2019

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.
Copy link
Member

@gafter gafter Nov 19, 2019

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;
Copy link
Member

@gafter gafter Nov 19, 2019

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");
}
}
}
}
Copy link
Member

@gafter gafter Nov 19, 2019

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

Copy link
Member

@gafter gafter left a 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).

Copy link
Member

@gafter gafter left a 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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

C# compiler should issue CS1961 for variance violations for local functions within default interface methods
3 participants