Skip to content

Commit 8d75b3e

Browse files
committed
Enable consumption of init-only properties in VB.
Closes #44870. Closes #49469.
1 parent 1ad38b2 commit 8d75b3e

54 files changed

Lines changed: 5552 additions & 47 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ internal bool CalculateUseSiteDiagnostic(ref DiagnosticInfo result)
933933

934934
// Check return type, custom modifiers, parameters
935935
if (DeriveUseSiteDiagnosticFromType(ref result, this.ReturnTypeWithAnnotations,
936-
MethodKind == MethodKind.PropertySet ?
936+
IsInitOnly ?
937937
AllowedRequiredModifierType.System_Runtime_CompilerServices_IsExternalInit :
938938
AllowedRequiredModifierType.None) ||
939939
DeriveUseSiteDiagnosticFromCustomModifiers(ref result, this.RefCustomModifiers, AllowedRequiredModifierType.System_Runtime_InteropServices_InAttribute) ||

src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3269,6 +3269,65 @@ void M2()
32693269
Assert.True(method.HasUnsupportedMetadata);
32703270
}
32713271

3272+
[Fact]
3273+
public void ModReqOnStaticSet()
3274+
{
3275+
string il = @"
3276+
.class public auto ansi beforefieldinit C extends System.Object
3277+
{
3278+
.method public hidebysig newslot specialname
3279+
static void modreq(System.Runtime.CompilerServices.IsExternalInit) set_P(int32 x) cil managed
3280+
{
3281+
IL_0000: ldnull
3282+
IL_0001: throw
3283+
}
3284+
3285+
.property instance int32 P()
3286+
{
3287+
.set void modreq(System.Runtime.CompilerServices.IsExternalInit) C::set_P(int32)
3288+
}
3289+
3290+
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
3291+
{
3292+
IL_0000: ldnull
3293+
IL_0001: throw
3294+
}
3295+
}
3296+
3297+
.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsExternalInit extends System.Object
3298+
{
3299+
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
3300+
{
3301+
IL_0000: ldnull
3302+
IL_0001: throw
3303+
}
3304+
}
3305+
";
3306+
string source = @"
3307+
public class D
3308+
{
3309+
void M2()
3310+
{
3311+
C.P = 2;
3312+
}
3313+
}
3314+
";
3315+
3316+
var reference = CreateMetadataReferenceFromIlSource(il);
3317+
var comp = CreateCompilation(source, references: new[] { reference }, parseOptions: TestOptions.Regular9);
3318+
comp.VerifyEmitDiagnostics(
3319+
// (6,11): error CS0570: 'C.P.set' is not supported by the language
3320+
// C.P = 2;
3321+
Diagnostic(ErrorCode.ERR_BindToBogus, "P").WithArguments("C.P.set").WithLocation(6, 11)
3322+
);
3323+
3324+
var method = (PEMethodSymbol)comp.GlobalNamespace.GetMember("C.set_P");
3325+
Assert.False(method.IsInitOnly);
3326+
Assert.False(method.GetPublicSymbol().IsInitOnly);
3327+
Assert.True(method.HasUseSiteError);
3328+
Assert.True(method.HasUnsupportedMetadata);
3329+
}
3330+
32723331
[Fact]
32733332
public void ModReqOnMethodParameter()
32743333
{

src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,12 @@ Friend Class MockMethodSymbol
534534
End Get
535535
End Property
536536

537+
Public Overrides ReadOnly Property IsInitOnly As Boolean
538+
Get
539+
Return False
540+
End Get
541+
End Property
542+
537543
Public Overrides ReadOnly Property IsVararg As Boolean
538544
Get
539545
Return False

src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ Public Class TestOptions
1111
Public Shared ReadOnly Script As New VisualBasicParseOptions(kind:=SourceCodeKind.Script)
1212
Public Shared ReadOnly Regular As New VisualBasicParseOptions(kind:=SourceCodeKind.Regular)
1313
Public Shared ReadOnly Regular15_5 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic15_5)
14+
Public Shared ReadOnly Regular16 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic16)
15+
Public Shared ReadOnly Regular16_9 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic16_9)
16+
Public Shared ReadOnly RegularLatest As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.Latest)
1417
Public Shared ReadOnly RegularWithLegacyStrongName As VisualBasicParseOptions = Regular.WithFeature("UseLegacyStrongNameProvider")
1518

1619
Public Shared ReadOnly ReleaseDll As VisualBasicCompilationOptions = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel:=OptimizationLevel.Release).WithParseOptions(Regular)

src/Compilers/VisualBasic/Portable/Binding/Binder_Attributes.vb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
484484
AccessCheck.GetAccessibilityForErrorMessage(setMethod, Me.Compilation.Assembly))
485485
hasErrors = True
486486
End If
487+
488+
If setMethod.IsInitOnly Then
489+
InternalSyntax.Parser.CheckFeatureAvailability(diagnostics,
490+
identifierName.Location,
491+
DirectCast(identifierName.SyntaxTree.Options, VisualBasicParseOptions).LanguageVersion,
492+
InternalSyntax.Feature.InitOnlySettersUsage)
493+
End If
487494
End If
488495

489496
Case Else

src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
978978
[property],
979979
propertyGroup,
980980
PropertyAccessKind.Unknown,
981-
[property].IsWritable(receiver, Me),
981+
[property].IsWritable(receiver, Me, isKnownTargetOfObjectMemberInintializer:=False),
982982
receiver,
983983
boundArguments,
984984
argumentInfo.DefaultArguments,
@@ -2905,6 +2905,18 @@ ProduceBoundNode:
29052905
outConversion, outPlaceholder,
29062906
targetType, copyBackExpression.HasErrors).MakeCompilerGenerated()
29072907
Else
2908+
Dim propertyAccess = TryCast(argument, BoundPropertyAccess)
2909+
2910+
If propertyAccess IsNot Nothing AndAlso propertyAccess.AccessKind <> PropertyAccessKind.Get AndAlso
2911+
propertyAccess.PropertySymbol.SetMethod?.IsInitOnly Then
2912+
2913+
Debug.Assert(Not propertyAccess.IsWriteable) ' Used to be writable prior to VB 16.9, which caused a use-site error while binding an assignment above.
2914+
InternalSyntax.Parser.CheckFeatureAvailability(diagnostics,
2915+
argument.Syntax.Location,
2916+
DirectCast(argument.Syntax.SyntaxTree.Options, VisualBasicParseOptions).LanguageVersion,
2917+
InternalSyntax.Feature.InitOnlySettersUsage)
2918+
End If
2919+
29082920
' Need to allocate a temp of the target type,
29092921
' init it with argument's value,
29102922
' pass it ByRef. Code gen will do this.

src/Compilers/VisualBasic/Portable/Binding/Binder_ObjectInitializer.vb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,12 +663,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
663663
target,
664664
diagnostics)
665665

666+
Dim propertyAccess = TryCast(target, BoundPropertyAccess)
667+
668+
If propertyAccess IsNot Nothing Then
669+
Debug.Assert(propertyAccess.AccessKind = PropertyAccessKind.Unknown)
670+
' See if we can reclassify access as writable given that this is an object initializer.
671+
' This is needed to accommodate init-only properties.
672+
If propertyAccess.AccessKind <> PropertyAccessKind.Get AndAlso Not propertyAccess.IsWriteable AndAlso
673+
propertyAccess.PropertySymbol.IsWritable(propertyAccess.ReceiverOpt, Me, isKnownTargetOfObjectMemberInintializer:=True) Then
674+
675+
propertyAccess = propertyAccess.Update(propertyAccess.PropertySymbol, propertyAccess.PropertyGroupOpt, propertyAccess.AccessKind, isWriteable:=True,
676+
propertyAccess.IsLValue, propertyAccess.ReceiverOpt, propertyAccess.Arguments, propertyAccess.DefaultArguments,
677+
propertyAccess.Type)
678+
target = propertyAccess
679+
End If
680+
End If
681+
666682
If Not target.HasErrors Then
667683
Dim isShared As Boolean
668684
If target.Kind = BoundKind.FieldAccess Then
669685
isShared = DirectCast(target, BoundFieldAccess).FieldSymbol.IsShared
670686
Else
671-
Dim [property] = DirectCast(target, BoundPropertyAccess).PropertySymbol
687+
Dim [property] = propertyAccess.PropertySymbol
672688
' Treat extension properties as Shared in this context so we generate
673689
' an error (BC30991) that such properties cannot be used in an initializer.
674690
' Currently, there is only one extension property, InternalXmlHelper.Value:

src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,33 +1914,47 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
19141914

19151915
Debug.Assert(propertyAccess.AccessKind <> PropertyAccessKind.Get)
19161916

1917+
Dim setMethod = propertySymbol.GetMostDerivedSetMethod()
1918+
19171919
If Not propertyAccess.IsWriteable Then
1918-
ReportDiagnostic(diagnostics, node, ERRID.ERR_NoSetProperty1, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
1920+
If setMethod Is Nothing Then
1921+
ReportDiagnostic(diagnostics, node, ERRID.ERR_NoSetProperty1, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
1922+
Else
1923+
Debug.Assert(setMethod.IsInitOnly)
1924+
ReportDiagnostic(diagnostics, node, ERRID.ERR_AssignmentInitOnly, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
1925+
End If
1926+
19191927
isError = True
1920-
Else
1921-
Dim setMethod = propertySymbol.GetMostDerivedSetMethod()
1928+
End If
19221929

1923-
' NOTE: the setMethod could not be present, while it would still be
1924-
' possible to write to the property in a case
1925-
' where the property is a getter-only autoproperty
1926-
' and the writing is happening in the corresponding constructor or initializer
1927-
If setMethod IsNot Nothing Then
1928-
ReportDiagnosticsIfObsoleteOrNotSupportedByRuntime(diagnostics, setMethod, node)
1930+
' NOTE: the setMethod could not be present, while it would still be
1931+
' possible to write to the property in a case
1932+
' where the property is a getter-only autoproperty
1933+
' and the writing is happening in the corresponding constructor or initializer
1934+
If setMethod IsNot Nothing Then
1935+
1936+
If propertyAccess.IsWriteable AndAlso setMethod.IsInitOnly Then
1937+
InternalSyntax.Parser.CheckFeatureAvailability(diagnostics,
1938+
node.Location,
1939+
DirectCast(node.SyntaxTree.Options, VisualBasicParseOptions).LanguageVersion,
1940+
InternalSyntax.Feature.InitOnlySettersUsage)
1941+
End If
19291942

1930-
If ReportUseSiteError(diagnostics, op1.Syntax, setMethod) Then
1931-
isError = True
1932-
Else
1933-
Dim accessThroughType = GetAccessThroughType(propertyAccess.ReceiverOpt)
1934-
Dim useSiteDiagnostics As HashSet(Of DiagnosticInfo) = Nothing
1943+
ReportDiagnosticsIfObsoleteOrNotSupportedByRuntime(diagnostics, setMethod, node)
19351944

1936-
If Not IsAccessible(setMethod, useSiteDiagnostics, accessThroughType) AndAlso
1937-
IsAccessible(propertySymbol, useSiteDiagnostics, accessThroughType) Then
1938-
ReportDiagnostic(diagnostics, node, ERRID.ERR_NoAccessibleSet, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
1939-
isError = True
1940-
End If
1945+
If ReportUseSiteError(diagnostics, op1.Syntax, setMethod) Then
1946+
isError = True
1947+
Else
1948+
Dim accessThroughType = GetAccessThroughType(propertyAccess.ReceiverOpt)
1949+
Dim useSiteDiagnostics As HashSet(Of DiagnosticInfo) = Nothing
19411950

1942-
diagnostics.Add(node, useSiteDiagnostics)
1951+
If Not IsAccessible(setMethod, useSiteDiagnostics, accessThroughType) AndAlso
1952+
IsAccessible(propertySymbol, useSiteDiagnostics, accessThroughType) Then
1953+
ReportDiagnostic(diagnostics, node, ERRID.ERR_NoAccessibleSet, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
1954+
isError = True
19431955
End If
1956+
1957+
diagnostics.Add(node, useSiteDiagnostics)
19441958
End If
19451959
End If
19461960

src/Compilers/VisualBasic/Portable/Binding/Binder_XmlLiterals.vb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,6 +1881,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
18811881
End Get
18821882
End Property
18831883

1884+
Public Overrides ReadOnly Property IsInitOnly As Boolean
1885+
Get
1886+
Return _originalDefinition.IsInitOnly
1887+
End Get
1888+
End Property
1889+
18841890
Public Overrides ReadOnly Property IsVararg As Boolean
18851891
Get
18861892
Return _originalDefinition.IsVararg

src/Compilers/VisualBasic/Portable/Errors/Errors.vb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
17521752
ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation = 37309
17531753
ERR_RuntimeDoesNotSupportProtectedAccessForInterfaceMember = 37310
17541754

1755+
ERR_AssignmentInitOnly = 37311
1756+
ERR_OverridingInitOnlyProperty = 37312
1757+
ERR_PropertyDoesntImplementInitOnly = 37313
1758+
17551759
'// WARNINGS BEGIN HERE
17561760
WRN_UseOfObsoleteSymbol2 = 40000
17571761
WRN_InvalidOverrideDueToTupleNames2 = 40001
@@ -2038,5 +2042,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
20382042
FEATURE_InterpolatedStrings
20392043
FEATURE_UnconstrainedTypeParameterInConditional
20402044
FEATURE_CommentsAfterLineContinuation
2045+
FEATURE_InitOnlySettersUsage
20412046
End Enum
20422047
End Namespace

0 commit comments

Comments
 (0)