Skip to content

Commit

Permalink
Merge "Default Interface Implementation of static members" feature in…
Browse files Browse the repository at this point in the history
…to 'main' #61437
  • Loading branch information
AlekseyTs authored May 20, 2022
2 parents 23f4924 + 22e3780 commit a51b65c
Show file tree
Hide file tree
Showing 78 changed files with 16,391 additions and 6,319 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ internal void CheckConstraintLanguageVersionAndRuntimeSupportForConversion(Synta

if (conversion.IsUserDefined && conversion.Method is MethodSymbol method && method.IsStatic)
{
if (method.IsAbstract)
if (method.IsAbstract || method.IsVirtual)
{
Debug.Assert(conversion.ConstrainedToTypeOpt is TypeParameterSymbol);

Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6383,7 +6383,7 @@ BoundExpression tryBindMemberAccessWithBoundTypeLeft(
if (leftType.TypeKind == TypeKind.TypeParameter)
{
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
this.LookupMembersWithFallback(lookupResult, leftType, rightName, rightArity, ref useSiteInfo, basesBeingResolved: null, options: options | LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstract);
this.LookupMembersWithFallback(lookupResult, leftType, rightName, rightArity, ref useSiteInfo, basesBeingResolved: null, options: options | LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstractOrVirtual);
diagnostics.Add(right, useSiteInfo);
if (lookupResult.IsMultiViable)
{
Expand Down Expand Up @@ -7184,7 +7184,7 @@ private void CheckReceiverAndRuntimeSupportForSymbolAccess(SyntaxNode node, Boun
{
if (symbol.ContainingType?.IsInterface == true)
{
if (symbol.IsStatic && symbol.IsAbstract)
if (symbol.IsStatic && (symbol.IsAbstract || symbol.IsVirtual))
{
Debug.Assert(symbol is not TypeSymbol);

Expand Down
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1311,7 +1311,7 @@ internal static ImmutableArray<Symbol> GetCandidateMembers(NamespaceOrTypeSymbol
/// </remarks>
internal SingleLookupResult CheckViability(Symbol symbol, int arity, LookupOptions options, TypeSymbol accessThroughType, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, ConsList<TypeSymbol> basesBeingResolved = null)
{
Debug.Assert((options & LookupOptions.MustBeAbstract) == 0 || (options & LookupOptions.MustNotBeInstance) != 0);
Debug.Assert((options & LookupOptions.MustBeAbstractOrVirtual) == 0 || (options & LookupOptions.MustNotBeInstance) != 0);
bool inaccessibleViaQualifier;
DiagnosticInfo diagInfo;

Expand All @@ -1327,8 +1327,8 @@ internal SingleLookupResult CheckViability(Symbol symbol, int arity, LookupOptio
{
return LookupResult.Empty();
}
else if ((options & (LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstract)) == (LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstract) &&
(unwrappedSymbol is not TypeSymbol && IsInstance(unwrappedSymbol) || !unwrappedSymbol.IsAbstract))
else if ((options & (LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstractOrVirtual)) == (LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstractOrVirtual) &&
(unwrappedSymbol is not TypeSymbol && IsInstance(unwrappedSymbol) || !(unwrappedSymbol.IsAbstract || unwrappedSymbol.IsVirtual)))
{
return LookupResult.Empty();
}
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,7 @@ private bool IsValidUserDefinedConditionalLogicalOperator(
bool typesAreSame = TypeSymbol.Equals(signature.LeftType, signature.RightType, TypeCompareKind.ConsiderEverything2) && TypeSymbol.Equals(signature.LeftType, signature.ReturnType, TypeCompareKind.ConsiderEverything2);
MethodSymbol definition;
bool typeMatchesContainer = TypeSymbol.Equals(signature.ReturnType.StrippedType(), t, TypeCompareKind.ConsiderEverything2) ||
(t.IsInterface && signature.Method.IsAbstract &&
(t.IsInterface && (signature.Method.IsAbstract || signature.Method.IsVirtual) &&
SourceUserDefinedOperatorSymbol.IsSelfConstrainedTypeParameter((definition = signature.Method.OriginalDefinition).ReturnType.StrippedType(), definition.ContainingType));

if (!typesAreSame || !typeMatchesContainer)
Expand Down Expand Up @@ -2329,7 +2329,7 @@ private bool CheckConstraintLanguageVersionAndRuntimeSupportForOperator(SyntaxNo

if (methodOpt?.ContainingType?.IsInterface == true && methodOpt.IsStatic)
{
if (methodOpt.IsAbstract)
if (methodOpt.IsAbstract || methodOpt.IsVirtual)
{
if (constrainedToTypeOpt is not TypeParameterSymbol)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/LookupOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ internal enum LookupOptions
MustNotBeMethodTypeParameter = 1 << 14,

/// <summary>
/// Consider only symbols that are abstract.
/// Consider only symbols that are abstract or virtual.
/// </summary>
MustBeAbstract = 1 << 15,
MustBeAbstractOrVirtual = 1 << 15,
}

internal static class LookupOptionExtensions
Expand Down
8 changes: 4 additions & 4 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -6575,7 +6575,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>extension GetEnumerator</value>
</data>
<data name="ERR_UnmanagedCallersOnlyRequiresStatic" xml:space="preserve">
<value>'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract methods or static local functions.</value>
<value>'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract, non-virtual methods or static local functions.</value>
<comment>UnmanagedCallersOnly is not localizable.</comment>
</data>
<data name="ERR_InvalidUnmanagedCallersOnlyCallConv" xml:space="preserve">
Expand Down Expand Up @@ -6911,7 +6911,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>Target runtime doesn't support static abstract members in interfaces.</value>
</data>
<data name="ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers" xml:space="preserve">
<value>The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract members.</value>
<value>The interface '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. The constraint interface '{1}' or its base interface has static abstract or virtual members.</value>
</data>
<data name="ERR_BadAbstractUnaryOperatorSignature" xml:space="preserve">
<value>The parameter of a unary operator must be the containing type, or its type parameter constrained to it.</value>
Expand All @@ -6929,10 +6929,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>The first operand of an overloaded shift operator must have the same type as the containing type or its type parameter constrained to it</value>
</data>
<data name="ERR_BadAbstractStaticMemberAccess" xml:space="preserve">
<value>A static abstract interface member can be accessed only on a type parameter.</value>
<value>A static virtual or abstract interface member can be accessed only on a type parameter.</value>
</data>
<data name="ERR_ExpressionTreeContainsAbstractStaticMemberAccess" xml:space="preserve">
<value>An expression tree may not contain an access of static abstract interface member</value>
<value>An expression tree may not contain an access of static virtual or abstract interface member</value>
</data>
<data name="ERR_CloseUnimplementedInterfaceMemberNotStatic" xml:space="preserve">
<value>'{0}' does not implement static interface member '{1}'. '{2}' cannot implement the interface member because it is not static.</value>
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/CodeGen/EmitConversion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ private void EmitDelegateCreation(BoundExpression node, BoundExpression receiver
{
_builder.EmitNullConstant();

if (method.IsAbstract)
if (method.IsAbstract || method.IsVirtual)
{
if (receiver is not BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } })
{
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1531,7 +1531,7 @@ private void EmitStaticCallExpression(BoundCall call, UseKind useKind)
EmitArguments(arguments, method.Parameters, call.ArgumentRefKindsOpt);
int stackBehavior = GetCallStackBehavior(method, arguments);

if (method.IsAbstract)
if (method.IsAbstract || method.IsVirtual)
{
if (receiver is not BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } })
{
Expand Down Expand Up @@ -3524,7 +3524,7 @@ private void EmitLoadFunction(BoundFunctionPointerLoad load, bool used)

if (used)
{
if (load.TargetMethod.IsAbstract && load.TargetMethod.IsStatic)
if ((load.TargetMethod.IsAbstract || load.TargetMethod.IsVirtual) && load.TargetMethod.IsStatic)
{
if (load.ConstrainedToTypeOpt is not { TypeKind: TypeKind.TypeParameter })
{
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,7 @@ public override BoundNode VisitCall(BoundCall node)

_counter += 1;

if (method.IsAbstract && receiver is BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } } typeExpression)
if ((method.IsAbstract || method.IsVirtual) && receiver is BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } } typeExpression)
{
receiver = typeExpression.Update(aliasOpt: null, boundContainingTypeOpt: null, boundDimensionsOpt: ImmutableArray<BoundExpression>.Empty,
typeWithAnnotations: typeExpression.TypeWithAnnotations, type: this.VisitType(typeExpression.Type));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ private void VisitCall(
{
Error(ErrorCode.ERR_RefReturningCallInExpressionTree, node);
}
else if (method.IsAbstract && method.IsStatic)
else if ((method.IsAbstract || method.IsVirtual) && method.IsStatic)
{
Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
}
Expand Down Expand Up @@ -506,7 +506,7 @@ public override BoundNode VisitPropertyAccess(BoundPropertyAccess node)
CheckRefReturningPropertyAccess(node, property);
CheckReceiverIfField(node.ReceiverOpt);

if (_inExpressionLambda && property.IsAbstract && property.IsStatic)
if (_inExpressionLambda && (property.IsAbstract || property.IsVirtual) && property.IsStatic)
{
Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
}
Expand Down Expand Up @@ -642,7 +642,7 @@ public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDe
var binary = node.LogicalOperator;
var unary = node.OperatorKind.Operator() == BinaryOperatorKind.And ? node.FalseOperator : node.TrueOperator;

if ((binary.IsAbstract && binary.IsStatic) || (unary.IsAbstract && unary.IsStatic))
if (((binary.IsAbstract || binary.IsVirtual) && binary.IsStatic) || ((unary.IsAbstract || unary.IsVirtual) && unary.IsStatic))
{
Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
}
Expand Down Expand Up @@ -673,7 +673,7 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
CheckLiftedUnaryOp(node);
CheckDynamic(node);

if (_inExpressionLambda && node.MethodOpt is MethodSymbol method && method.IsAbstract && method.IsStatic)
if (_inExpressionLambda && node.MethodOpt is MethodSymbol method && (method.IsAbstract || method.IsVirtual) && method.IsStatic)
{
Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
}
Expand Down Expand Up @@ -770,7 +770,7 @@ public override BoundNode VisitConversion(BoundConversion node)

default:

if (_inExpressionLambda && node.Conversion.Method is MethodSymbol method && method.IsAbstract && method.IsStatic)
if (_inExpressionLambda && node.Conversion.Method is MethodSymbol method && (method.IsAbstract || method.IsVirtual) && method.IsStatic)
{
Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
}
Expand Down Expand Up @@ -830,7 +830,7 @@ private void CheckMethodGroup(BoundMethodGroup node, MethodSymbol method, bool p
{
Error(ErrorCode.ERR_AddressOfMethodGroupInExpressionTree, node);
}
else if (method is not null && method.IsAbstract && method.IsStatic)
else if (method is not null && (method.IsAbstract || method.IsVirtual) && method.IsStatic)
{
Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ private void CheckBinaryOperator(BoundBinaryOperator node)
{
Error(ErrorCode.ERR_FeatureNotValidInExpressionTree, node, method);
}
else if (method.IsAbstract && method.IsStatic)
else if ((method.IsAbstract || method.IsVirtual) && method.IsStatic)
{
Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ internal FieldSymbol GetOrAddCacheField(SyntheticBoundNodeFactory factory, Bound
Debug.Assert(delegateType.IsDelegateType());
Debug.Assert(targetMethod is { });

var constrainedToTypeOpt = (targetMethod.IsAbstract && boundDelegateCreation.Argument is BoundTypeExpression typeExpression) ? typeExpression.Type : null;
var constrainedToTypeOpt = ((targetMethod.IsAbstract || targetMethod.IsVirtual) && boundDelegateCreation.Argument is BoundTypeExpression typeExpression) ? typeExpression.Type : null;

if (_delegateFields.TryGetValue((constrainedToTypeOpt, delegateType, targetMethod), out var field))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ private static bool TryGetOwnerFunction(MethodSymbol currentFunction, BoundDeleg
var usedTypeParameters = PooledHashSet<TypeParameterSymbol>.GetInstance();
try
{
if (targetMethod.IsAbstract && boundDelegateCreation.Argument is BoundTypeExpression typeExpression)
if ((targetMethod.IsAbstract || targetMethod.IsVirtual) && boundDelegateCreation.Argument is BoundTypeExpression typeExpression)
{
FindTypeParameters(typeExpression.Type, usedTypeParameters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,9 +510,11 @@ private BoundExpression MakeConversionNodeCore(
case ConversionKind.MethodGroup when oldNodeOpt is { Type: { TypeKind: TypeKind.FunctionPointer } funcPtrType }:
{
var mg = (BoundMethodGroup)rewrittenOperand;
Debug.Assert(oldNodeOpt.SymbolOpt is { });
return new BoundFunctionPointerLoad(oldNodeOpt.Syntax, oldNodeOpt.SymbolOpt,
constrainedToTypeOpt: oldNodeOpt.SymbolOpt.IsStatic && oldNodeOpt.SymbolOpt.IsAbstract ? mg.ReceiverOpt?.Type : null,
MethodSymbol? symbolOpt = oldNodeOpt.SymbolOpt;
Debug.Assert(symbolOpt is { });
return new BoundFunctionPointerLoad(oldNodeOpt.Syntax, symbolOpt,
constrainedToTypeOpt: symbolOpt.IsStatic &&
(symbolOpt.IsAbstract || symbolOpt.IsVirtual) ? mg.ReceiverOpt?.Type : null,
type: funcPtrType, hasErrors: false);
}

Expand All @@ -525,7 +527,7 @@ private BoundExpression MakeConversionNodeCore(
Debug.Assert(method is { });
var oldSyntax = _factory.Syntax;
_factory.Syntax = (mg.ReceiverOpt ?? mg).Syntax;
var receiver = (!method.RequiresInstanceReceiver && !oldNodeOpt.IsExtensionMethod && !method.IsAbstract) ? _factory.Type(method.ContainingType) : mg.ReceiverOpt;
var receiver = (!method.RequiresInstanceReceiver && !oldNodeOpt.IsExtensionMethod && !method.IsAbstract && !method.IsVirtual) ? _factory.Type(method.ContainingType) : mg.ReceiverOpt;
Debug.Assert(receiver is { });
_factory.Syntax = oldSyntax;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE
Debug.Assert(method is { });
var oldSyntax = _factory.Syntax;
_factory.Syntax = (mg.ReceiverOpt ?? mg).Syntax;
var receiver = (!method.RequiresInstanceReceiver && !node.IsExtensionMethod && !method.IsAbstract) ? _factory.Type(method.ContainingType) : VisitExpression(mg.ReceiverOpt)!;
var receiver = (!method.RequiresInstanceReceiver && !node.IsExtensionMethod && !method.IsAbstract && !method.IsVirtual) ? _factory.Type(method.ContainingType) : VisitExpression(mg.ReceiverOpt)!;
_factory.Syntax = oldSyntax;
return node.Update(receiver, method, node.IsExtensionMethod, node.WasTargetTyped, node.Type);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1287,7 +1287,7 @@ private static bool SelfOrBaseHasStaticAbstractMember(TypeWithAnnotations constr
{
Debug.Assert(constraintType.Type.IsInterfaceType());

Func<Symbol, bool> predicate = static m => m.IsStatic && m.IsAbstract;
Func<Symbol, bool> predicate = static m => m.IsStatic && (m.IsAbstract || m.IsVirtual);
var definition = (NamedTypeSymbol)constraintType.Type.OriginalDefinition;

if (definition.GetMembersUnordered().Any(predicate))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ public override int Arity
// Final is a little special - if a method has the virtual, newslot, and final attr
// (and is not an explicit override) then we treat it as non-virtual for C# purposes.
public override bool IsVirtual => this.IsMetadataVirtual() && !this.IsDestructor && !this.IsMetadataFinal && !this.IsAbstract &&
(this._containingType.IsInterface ? this.IsMetadataNewSlot() : !this.IsOverride);
(this._containingType.IsInterface ? (this.IsStatic || this.IsMetadataNewSlot()) : !this.IsOverride);

// Has to be metadata virtual and cannot be a destructor.
// Must either lack the newslot flag or be an explicit override (i.e. via the MethodImpl table).
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ internal bool CheckAndReportValidUnmanagedCallersOnlyTarget(Location? location,
{
Debug.Assert((location == null) == (diagnostics == null));

if (!IsStatic || IsAbstract || MethodKind is not (MethodKind.Ordinary or MethodKind.LocalFunction))
if (!IsStatic || IsAbstract || IsVirtual || MethodKind is not (MethodKind.Ordinary or MethodKind.LocalFunction))
{
// `UnmanagedCallersOnly` can only be applied to ordinary static methods or local functions.
diagnostics?.Add(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, location!);
Expand Down
Loading

0 comments on commit a51b65c

Please sign in to comment.