Skip to content

Commit

Permalink
Support declaring static fields, auto-properties and field-like event…
Browse files Browse the repository at this point in the history
…s within interfaces. (#20165)
  • Loading branch information
AlekseyTs authored Jun 13, 2017
1 parent 045940d commit b2a061e
Show file tree
Hide file tree
Showing 13 changed files with 1,314 additions and 127 deletions.
2 changes: 2 additions & 0 deletions docs/features/DefaultInterfaceImplementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class Test1 : I1

- Implementing interface events in derived interfaces by using explicit implementation syntax, accessibility is private, no allowed modifiers.

- Declaring static fields, auto-properties and field-like events (**protected** modifier is not allowed).


**Open issues and work items** are tracked in https://github.com/dotnet/roslyn/issues/17952.

Expand Down
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@
<value>Event is never used</value>
</data>
<data name="ERR_InterfaceEventInitializer" xml:space="preserve">
<value>'{0}': event in interface cannot have initializer</value>
<value>'{0}': instance event in interface cannot have initializer</value>
</data>
<data name="ERR_BadEventUsage" xml:space="preserve">
<value>The event '{0}' can only appear on the left hand side of += or -= (except when used from within the type '{1}')</value>
Expand Down Expand Up @@ -1464,7 +1464,7 @@ If such a class is used as a base class and if the deriving class defines a dest
<value>Struct member '{0}' of type '{1}' causes a cycle in the struct layout</value>
</data>
<data name="ERR_InterfacesCantContainFields" xml:space="preserve">
<value>Interfaces cannot contain fields</value>
<value>Interfaces cannot contain instance fields</value>
</data>
<data name="ERR_InterfacesCantContainConstructors" xml:space="preserve">
<value>Interfaces cannot contain constructors</value>
Expand Down Expand Up @@ -4580,7 +4580,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>Auto-implemented properties must override all accessors of the overridden property.</value>
</data>
<data name="ERR_AutoPropertyInitializerInInterface" xml:space="preserve">
<value>Auto-implemented properties inside interfaces cannot have initializers.</value>
<value>Instance auto-implemented properties inside interfaces cannot have initializers.</value>
</data>
<data name="ERR_InitializerInStructWithoutExplicitConstructor" xml:space="preserve">
<value>Structs without explicit constructors cannot contain members with initializers.</value>
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,8 @@ private static SyntaxToken GetImplicitConstructorBodyToken(CSharpSyntaxNode cont
{
case SyntaxKind.ClassDeclaration:
return ((ClassDeclarationSyntax)containerNode).OpenBraceToken;
case SyntaxKind.InterfaceDeclaration:
return ((InterfaceDeclarationSyntax)containerNode).OpenBraceToken;
case SyntaxKind.StructDeclaration:
return ((StructDeclarationSyntax)containerNode).OpenBraceToken;
case SyntaxKind.EnumDeclaration:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,7 @@ internal SourceCustomEventAccessorSymbol(
isExtensionMethod: false,
isMetadataVirtualIgnoringModifiers: explicitInterfaceImplementations.Any());

if (@event.ContainingType.IsInterface)
{
Binder.CheckFeatureAvailability(syntax, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, this.Location);

if (!ContainingAssembly.RuntimeSupportsDefaultInterfaceImplementation)
{
diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, this.Location);
}
}
CheckFeatureAvailabilityAndRuntimeSupport(syntax, this.Location, hasBody: true, diagnostics: diagnostics);

if (syntax.Body != null || syntax.ExpressionBody != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy

if (hasInitializer)
{
if (inInterfaceType)
if (inInterfaceType && !this.IsStatic)
{
diagnostics.Add(ErrorCode.ERR_InterfaceEventInitializer, this.Locations[0], this);
}
Expand All @@ -74,13 +74,13 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy

// NOTE: if there's an initializer in source, we'd better create a backing field, regardless of
// whether or not the initializer is legal.
if (hasInitializer || !(inInterfaceType || this.IsExtern || this.IsAbstract))
if (hasInitializer || !(this.IsExtern || this.IsAbstract))
{
_associatedField = MakeAssociatedField(declaratorSyntax);
// Don't initialize this.type - we'll just use the type of the field (which is lazy and handles var)
}

if (inInterfaceType)
if (inInterfaceType && !this.IsStatic)
{
if (this.IsExtern)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2253,6 +2253,7 @@ private MembersAndInitializers BuildMembersAndInitializers(DiagnosticBag diagnos
break;

case TypeKind.Class:
case TypeKind.Interface:
case TypeKind.Submission:
// No additional checking required.
AddSynthesizedConstructorsIfNecessary(builder.NonTypeNonIndexerMembers, builder.StaticInitializers, diagnostics);
Expand Down Expand Up @@ -2689,20 +2690,21 @@ private static void CheckInterfaceMember(Symbol member, DiagnosticBag diagnostic
switch (member.Kind)
{
case SymbolKind.Field:
if ((object)((FieldSymbol)member).AssociatedSymbol == null)
{
diagnostics.Add(ErrorCode.ERR_InterfacesCantContainFields, member.Locations[0]);
}
break;

case SymbolKind.Method:
var meth = (MethodSymbol)member;
switch (meth.MethodKind)
{
case MethodKind.Constructor:
case MethodKind.StaticConstructor:
diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConstructors, member.Locations[0]);
break;
case MethodKind.StaticConstructor:
if (!meth.IsImplicitlyDeclared)
{
diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConstructors, member.Locations[0]);
}
break;
case MethodKind.Conversion:
case MethodKind.UserDefinedOperator:
diagnostics.Add(ErrorCode.ERR_InterfacesCantContainOperators, member.Locations[0]);
Expand Down Expand Up @@ -2812,7 +2814,7 @@ private void AddSynthesizedConstructorsIfNecessary(ArrayBuilder<Symbol> members,
// NOTE: Per section 11.3.8 of the spec, "every struct implicitly has a parameterless instance constructor".
// We won't insert a parameterless constructor for a struct if there already is one.
// We don't expect anything to be emitted, but it should be in the symbol table.
if ((!hasParameterlessInstanceConstructor && this.IsStructType()) || (!hasInstanceConstructor && !this.IsStatic))
if ((!hasParameterlessInstanceConstructor && this.IsStructType()) || (!hasInstanceConstructor && !this.IsStatic && !this.IsInterface))
{
members.Add((this.TypeKind == TypeKind.Submission) ?
new SynthesizedSubmissionConstructor(this, diagnostics) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ internal static DeclarationModifiers MakeModifiers(NamedTypeSymbol containingTyp
DeclarationModifiers.Unsafe |
DeclarationModifiers.Abstract; // filtered out later

if (containingType.IsInterface)
{
allowedModifiers &= ~(DeclarationModifiers.Protected | DeclarationModifiers.ProtectedInternal);
}

var errorLocation = new SourceLocation(firstIdentifier);
DeclarationModifiers result = ModifierUtils.MakeAndCheckNontypeMemberModifiers(
modifiers, defaultAccess, allowedModifiers, errorLocation, diagnostics, out modifierErrors);
Expand Down Expand Up @@ -295,6 +300,18 @@ internal SourceMemberFieldSymbolFromDeclarator(
{
this.ReportModifiersDiagnostics(diagnostics);
}

if (containingType.IsInterface)
{
if (this.IsStatic)
{
Binder.CheckFeatureAvailability(declarator, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, ErrorLocation);
}
else
{
diagnostics.Add(ErrorCode.ERR_InterfacesCantContainFields, ErrorLocation);
}
}
}

protected sealed override TypeSyntax TypeSyntax
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1586,10 +1586,8 @@ protected void CheckFeatureAvailabilityAndRuntimeSupport(SyntaxNode declarationS
Binder.CheckFeatureAvailability(declarationSyntax, MessageID.IDS_DefaultInterfaceImplementation, diagnostics, location);
}

if ((hasBody || IsExtern) && !ContainingAssembly.RuntimeSupportsDefaultInterfaceImplementation)
if ((hasBody || IsExtern) && !IsStatic && !ContainingAssembly.RuntimeSupportsDefaultInterfaceImplementation)
{
// PROTOTYPE(DefaultInterfaceImplementation): It looks like some flavor of static members was supported by
// legacy runtimes. Should we suppress this error for them?
diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, location);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private SourcePropertySymbol(

this.CheckModifiers(location, isIndexer, diagnostics);

isAutoProperty = isAutoProperty && (!containingType.IsInterface && !IsAbstract && !IsExtern && !isIndexer && hasAccessorList);
isAutoProperty = isAutoProperty && (!(containingType.IsInterface && !IsStatic) && !IsAbstract && !IsExtern && !isIndexer && hasAccessorList);

if (isIndexer && !isExplicitInterfaceImplementation)
{
Expand Down Expand Up @@ -420,7 +420,7 @@ private void CheckInitializer(
Location location,
DiagnosticBag diagnostics)
{
if (_containingType.IsInterface)
if (_containingType.IsInterface && !IsStatic)
{
diagnostics.Add(ErrorCode.ERR_AutoPropertyInitializerInInterface, location, this);
}
Expand Down
Loading

0 comments on commit b2a061e

Please sign in to comment.