diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index c1351b967aa9b..ea6540e00678e 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -782,7 +782,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType, bool inExpressionTr } var lambdaParameters = lambdaSymbol.Parameters; - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, lambdaParameters, diagnostics, modifyCompilation: false); + ParameterHelpers.EnsureRefKindAttributesExist(compilation, lambdaParameters, diagnostics, modifyCompilation: false); if (returnType.HasType) { diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs index 892def2524919..4283b4512e2b2 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs @@ -34,6 +34,7 @@ internal abstract class PEAssemblyBuilderBase : PEModuleBuilder, Cci.IAssemblyRe private SynthesizedEmbeddedAttributeSymbol _lazyEmbeddedAttribute; private SynthesizedEmbeddedAttributeSymbol _lazyIsReadOnlyAttribute; + private SynthesizedEmbeddedAttributeSymbol _lazyRequiresLocationAttribute; private SynthesizedEmbeddedAttributeSymbol _lazyIsByRefLikeAttribute; private SynthesizedEmbeddedAttributeSymbol _lazyIsUnmanagedAttribute; private SynthesizedEmbeddedNullableAttributeSymbol _lazyNullableAttribute; @@ -94,6 +95,7 @@ internal sealed override ImmutableArray GetEmbeddedTypes(Bindin builder.AddIfNotNull(_lazyEmbeddedAttribute); builder.AddIfNotNull(_lazyIsReadOnlyAttribute); + builder.AddIfNotNull(_lazyRequiresLocationAttribute); builder.AddIfNotNull(_lazyIsUnmanagedAttribute); builder.AddIfNotNull(_lazyIsByRefLikeAttribute); builder.AddIfNotNull(_lazyNullableAttribute); @@ -292,6 +294,19 @@ protected override SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute() return base.TrySynthesizeIsReadOnlyAttribute(); } + protected override SynthesizedAttributeData TrySynthesizeRequiresLocationAttribute() + { + if ((object)_lazyRequiresLocationAttribute != null) + { + return new SynthesizedAttributeData( + _lazyRequiresLocationAttribute.Constructors[0], + ImmutableArray.Empty, + ImmutableArray>.Empty); + } + + return base.TrySynthesizeRequiresLocationAttribute(); + } + protected override SynthesizedAttributeData TrySynthesizeIsUnmanagedAttribute() { if ((object)_lazyIsUnmanagedAttribute != null) @@ -356,6 +371,15 @@ private void CreateEmbeddedAttributesIfNeeded(BindingDiagnosticBag diagnostics) createParameterlessEmbeddedAttributeSymbol); } + if ((needsAttributes & EmbeddableAttributes.RequiresLocationAttribute) != 0) + { + CreateAttributeIfNeeded( + ref _lazyRequiresLocationAttribute, + diagnostics, + AttributeDescription.RequiresLocationAttribute, + createParameterlessEmbeddedAttributeSymbol); + } + if ((needsAttributes & EmbeddableAttributes.IsByRefLikeAttribute) != 0) { CreateAttributeIfNeeded( diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index 27702b3664a69..7831974982a40 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -1557,6 +1557,18 @@ internal SynthesizedAttributeData SynthesizeIsReadOnlyAttribute(Symbol symbol) return TrySynthesizeIsReadOnlyAttribute(); } + internal SynthesizedAttributeData SynthesizeRequiresLocationAttribute(ParameterSymbol symbol) + { + if ((object)Compilation.SourceModule != symbol.ContainingModule) + { + // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute. + Debug.Assert(false); // PROTOTYPE: Test this code path. + return null; + } + + return TrySynthesizeRequiresLocationAttribute(); + } + internal SynthesizedAttributeData SynthesizeIsUnmanagedAttribute(Symbol symbol) { if ((object)Compilation.SourceModule != symbol.ContainingModule) @@ -1763,6 +1775,12 @@ protected virtual SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute() return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor); } + protected virtual SynthesizedAttributeData TrySynthesizeRequiresLocationAttribute() + { + // For modules, this attribute should be present. Only assemblies generate and embed this type. + return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_RequiresLocationAttribute__ctor); + } + protected virtual SynthesizedAttributeData TrySynthesizeIsUnmanagedAttribute() { // For modules, this attribute should be present. Only assemblies generate and embed this type. @@ -1796,6 +1814,11 @@ internal void EnsureIsReadOnlyAttributeExists() EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsReadOnlyAttribute); } + internal void EnsureRequiresLocationAttributeExists() + { + EnsureEmbeddableAttributeExists(EmbeddableAttributes.RequiresLocationAttribute); + } + internal void EnsureIsUnmanagedAttributeExists() { EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsUnmanagedAttribute); diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs index 94d75be8c52a3..01b23b262cc75 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs @@ -123,7 +123,7 @@ private void EnsureAttributesExist(TypeCompilationState compilationState) moduleBuilder.EnsureIsReadOnlyAttributeExists(); } - ParameterHelpers.EnsureIsReadOnlyAttributeExists(moduleBuilder, Parameters); + ParameterHelpers.EnsureRefKindAttributesExist(moduleBuilder, Parameters); if (moduleBuilder.Compilation.ShouldEmitNativeIntegerAttributes()) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs index e6de1d3cc4465..62544822281a3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs @@ -767,6 +767,7 @@ internal FieldSymbol DefineCallSiteStorageSymbol(NamedTypeSymbol containerDefini } } + // PROTOTYPE: RefKindVector does not support RefReadOnlyParameter. RefKindVector byRefs; if (hasByRefs) { diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 9aeeb3524e212..2344491da9c72 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -1143,6 +1143,12 @@ private void AddParameterRefKind(RefKind refKind) AddKeyword(SyntaxKind.InKeyword); AddSpace(); break; + case RefKind.RefReadOnlyParameter: + AddKeyword(SyntaxKind.RefKeyword); + AddSpace(); + AddKeyword(SyntaxKind.ReadOnlyKeyword); + AddSpace(); + break; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs index 81b42902a3dfe..1ec1ccc1de5d0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs @@ -323,7 +323,8 @@ static bool hasDefaultScope(bool useUpdatedEscapeRules, AnonymousTypeField field static bool isValidTypeArgument(bool useUpdatedEscapeRules, AnonymousTypeField field, ref bool needsIndexedName) { - needsIndexedName = needsIndexedName || field.IsParams || field.DefaultValue is not null; + // PROTOTYPE: Instead of using indexed name for delegates with `ref readonly` parameters, expand RefKindVector to use more bits? + needsIndexedName = needsIndexedName || field.IsParams || field.DefaultValue is not null || field.RefKind == RefKind.RefReadOnlyParameter; return hasDefaultScope(useUpdatedEscapeRules, field) && field.Type is { } type && !type.IsPointerOrFunctionPointer() && diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index d7304cff1db1f..87bb2c2e2fb9c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -526,6 +526,11 @@ internal void EnsureIsReadOnlyAttributeExists(BindingDiagnosticBag? diagnostics, EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsReadOnlyAttribute, diagnostics, location, modifyCompilation); } + internal void EnsureRequiresLocationAttributeExists(BindingDiagnosticBag? diagnostics, Location location, bool modifyCompilation) + { + EnsureEmbeddableAttributeExists(EmbeddableAttributes.RequiresLocationAttribute, diagnostics, location, modifyCompilation); + } + internal void EnsureIsByRefLikeAttributeExists(BindingDiagnosticBag? diagnostics, Location location, bool modifyCompilation) { EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsByRefLikeAttribute, diagnostics, location, modifyCompilation); @@ -629,6 +634,13 @@ internal bool CheckIfAttributeShouldBeEmbedded(EmbeddableAttributes attribute, B WellKnownType.System_Runtime_CompilerServices_RefSafetyRulesAttribute, WellKnownMember.System_Runtime_CompilerServices_RefSafetyRulesAttribute__ctor); + case EmbeddableAttributes.RequiresLocationAttribute: + return CheckIfAttributeShouldBeEmbedded( + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_RequiresLocationAttribute, + WellKnownMember.System_Runtime_CompilerServices_RequiresLocationAttribute__ctor); + default: throw ExceptionUtilities.UnexpectedValue(attribute); } diff --git a/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs index db18cf30be49f..8363b9e8f8bc0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/EmbeddableAttributes.cs @@ -18,5 +18,6 @@ internal enum EmbeddableAttributes NativeIntegerAttribute = 0x40, ScopedRefAttribute = 0x80, RefSafetyRulesAttribute = 0x100, + RequiresLocationAttribute = 0x200, } } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs index 46ab793940098..151310add8978 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs @@ -74,7 +74,7 @@ internal int MethodHashCode() public override bool IsImplicitlyDeclared => true; internal override MarshalPseudoCustomAttributeData? MarshallingInformation => null; internal override bool IsMetadataOptional => false; - internal override bool IsMetadataIn => RefKind == RefKind.In; + internal override bool IsMetadataIn => RefKind is RefKind.In or RefKind.RefReadOnlyParameter; internal override bool IsMetadataOut => RefKind == RefKind.Out; internal override ConstantValue? ExplicitDefaultConstantValue => null; internal override bool IsIDispatchConstant => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index d6fe6ae2cd9b9..52e27a5cb8611 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -39,32 +39,32 @@ private enum WellKnownAttributeFlags private struct PackedFlags { // Layout: - // |.|u|ss|fffffffff|n|rr|cccccccc|vvvvvvvv| + // |u|ss|fffffffff|n|rrr|cccccccc|vvvvvvvv| // // v = decoded well known attribute values. 8 bits. // c = completion states for well known attributes. 1 if given attribute has been decoded, 0 otherwise. 8 bits. - // r = RefKind. 2 bits. + // r = RefKind. 3 bits. // n = hasNameInMetadata. 1 bit. // f = FlowAnalysisAnnotations. 9 bits (8 value bits + 1 completion bit). // s = Scope. 2 bits. // u = HasUnscopedRefAttribute. 1 bit. - // Current total = 30 bits. + // Current total = 32 bits. private const int WellKnownAttributeDataOffset = 0; private const int WellKnownAttributeCompletionFlagOffset = 8; private const int RefKindOffset = 16; - private const int FlowAnalysisAnnotationsOffset = 20; - private const int ScopeOffset = 28; + private const int FlowAnalysisAnnotationsOffset = 21; + private const int ScopeOffset = 29; - private const int RefKindMask = 0x3; + private const int RefKindMask = 0x7; private const int WellKnownAttributeDataMask = 0xFF; private const int WellKnownAttributeCompletionFlagMask = WellKnownAttributeDataMask; private const int FlowAnalysisAnnotationsMask = 0xFF; private const int ScopeMask = 0x3; - private const int HasNameInMetadataBit = 0x1 << 18; - private const int FlowAnalysisAnnotationsCompletionBit = 0x1 << 19; - private const int HasUnscopedRefAttributeBit = 0x1 << 30; + private const int HasNameInMetadataBit = 0x1 << 19; + private const int FlowAnalysisAnnotationsCompletionBit = 0x1 << 20; + private const int HasUnscopedRefAttributeBit = 0x1 << 31; private const int AllWellKnownAttributesCompleteNoData = WellKnownAttributeCompletionFlagMask << WellKnownAttributeCompletionFlagOffset; @@ -230,6 +230,7 @@ private PEParameterSymbol( ParameterHandle handle, Symbol nullableContext, int countOfCustomModifiers, + bool isReturn, out bool isBad) { Debug.Assert((object)moduleSymbol != null); @@ -280,6 +281,10 @@ private PEParameterSymbol( { refKind = RefKind.Out; } + else if (!isReturn && moduleSymbol.Module.HasRequiresLocationAttribute(handle)) + { + refKind = RefKind.RefReadOnlyParameter; + } else if (moduleSymbol.Module.HasIsReadOnlyAttribute(handle)) { refKind = RefKind.In; @@ -375,19 +380,20 @@ private static PEParameterSymbol Create( var typeWithModifiers = TypeWithAnnotations.Create(type, customModifiers: CSharpCustomModifier.Convert(customModifiers)); PEParameterSymbol parameter = customModifiers.IsDefaultOrEmpty && refCustomModifiers.IsDefaultOrEmpty - ? new PEParameterSymbol(moduleSymbol, containingSymbol, ordinal, isByRef, typeWithModifiers, handle, nullableContext, 0, out isBad) - : new PEParameterSymbolWithCustomModifiers(moduleSymbol, containingSymbol, ordinal, isByRef, refCustomModifiers, typeWithModifiers, handle, nullableContext, out isBad); + ? new PEParameterSymbol(moduleSymbol, containingSymbol, ordinal, isByRef, typeWithModifiers, handle, nullableContext, 0, isReturn: isReturn, out isBad) + : new PEParameterSymbolWithCustomModifiers(moduleSymbol, containingSymbol, ordinal, isByRef, refCustomModifiers, typeWithModifiers, handle, nullableContext, isReturn: isReturn, out isBad); bool hasInAttributeModifier = parameter.RefCustomModifiers.HasInAttributeModifier(); if (isReturn) { // A RefReadOnly return parameter should always have this modreq, and vice versa. + Debug.Assert(parameter.RefKind != RefKind.RefReadOnlyParameter); isBad |= (parameter.RefKind == RefKind.RefReadOnly) != hasInAttributeModifier; } - else if (parameter.RefKind == RefKind.In) + else if (parameter.RefKind is RefKind.In or RefKind.RefReadOnlyParameter) { - // An in parameter should not have this modreq, unless the containing symbol was virtual or abstract. + // An in/ref readonly parameter should not have this modreq, unless the containing symbol was virtual or abstract. isBad |= isContainingSymbolVirtual != hasInAttributeModifier; } else if (hasInAttributeModifier) @@ -412,10 +418,11 @@ public PEParameterSymbolWithCustomModifiers( TypeWithAnnotations type, ParameterHandle handle, Symbol nullableContext, + bool isReturn, out bool isBad) : base(moduleSymbol, containingSymbol, ordinal, isByRef, type, handle, nullableContext, refCustomModifiers.NullToEmpty().Length + type.CustomModifiers.Length, - out isBad) + isReturn: isReturn, out isBad) { _refCustomModifiers = CSharpCustomModifier.Convert(refCustomModifiers); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 9b69ee7fb6bbb..13188452982d1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -125,7 +125,7 @@ internal void GetDeclarationDiagnostics(BindingDiagnosticBag addTo) GetReturnTypeAttributes(); var compilation = DeclaringCompilation; - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, addTo, modifyCompilation: false); + ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, addTo, modifyCompilation: false); ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, addTo, modifyCompilation: false); ParameterHelpers.EnsureScopedRefAttributeExists(compilation, Parameters, addTo, modifyCompilation: false); ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, addTo, modifyCompilation: false); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 2c2b8a64f179c..af0a6b8bdbd55 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -88,7 +88,8 @@ public static ImmutableArray MakeFunctionPointer Debug.Assert(addRefReadOnlyModifier, "If addReadonlyRef isn't true, we must have found a different location to encode the readonlyness of a function pointer"); ImmutableArray customModifiers = refKind switch { - RefKind.In => CreateInModifiers(binder, diagnostics, syntax), + // PROTOTYPE: Confirm encoding of ref readonly parameters in function pointers. + RefKind.In or RefKind.RefReadOnlyParameter => CreateInModifiers(binder, diagnostics, syntax), RefKind.Out => CreateOutModifiers(binder, diagnostics, syntax), _ => ImmutableArray.Empty }; @@ -216,12 +217,12 @@ private static ImmutableArray MakeParameters parameters) + internal static void EnsureRefKindAttributesExist(PEModuleBuilder moduleBuilder, ImmutableArray parameters) { - EnsureIsReadOnlyAttributeExists(moduleBuilder.Compilation, parameters, diagnostics: null, modifyCompilation: false, moduleBuilder); + EnsureRefKindAttributesExist(moduleBuilder.Compilation, parameters, diagnostics: null, modifyCompilation: false, moduleBuilder); } - internal static void EnsureIsReadOnlyAttributeExists(CSharpCompilation? compilation, ImmutableArray parameters, BindingDiagnosticBag diagnostics, bool modifyCompilation) + internal static void EnsureRefKindAttributesExist(CSharpCompilation? compilation, ImmutableArray parameters, BindingDiagnosticBag diagnostics, bool modifyCompilation) { // These parameters might not come from a compilation (example: lambdas evaluated in EE). // During rewriting, lowering will take care of flagging the appropriate PEModuleBuilder instead. @@ -230,10 +231,10 @@ internal static void EnsureIsReadOnlyAttributeExists(CSharpCompilation? compilat return; } - EnsureIsReadOnlyAttributeExists(compilation, parameters, diagnostics, modifyCompilation, moduleBuilder: null); + EnsureRefKindAttributesExist(compilation, parameters, diagnostics, modifyCompilation, moduleBuilder: null); } - private static void EnsureIsReadOnlyAttributeExists(CSharpCompilation compilation, ImmutableArray parameters, BindingDiagnosticBag? diagnostics, bool modifyCompilation, PEModuleBuilder? moduleBuilder) + private static void EnsureRefKindAttributesExist(CSharpCompilation compilation, ImmutableArray parameters, BindingDiagnosticBag? diagnostics, bool modifyCompilation, PEModuleBuilder? moduleBuilder) { foreach (var parameter in parameters) { @@ -248,6 +249,17 @@ private static void EnsureIsReadOnlyAttributeExists(CSharpCompilation compilatio compilation.EnsureIsReadOnlyAttributeExists(diagnostics, GetParameterLocation(parameter), modifyCompilation); } } + else if (parameter.RefKind == RefKind.RefReadOnlyParameter) + { + if (moduleBuilder is { }) + { + moduleBuilder.EnsureRequiresLocationAttributeExists(); + } + else + { + compilation.EnsureRequiresLocationAttributeExists(diagnostics, GetParameterLocation(parameter), modifyCompilation); + } + } } } @@ -945,6 +957,12 @@ internal static RefKind GetModifiers(SyntaxTokenList modifiers, out SyntaxToken Debug.Assert(refKind == RefKind.None); isScoped = true; break; + case SyntaxKind.ReadOnlyKeyword: + if (refKind == RefKind.Ref && refnessKeyword.GetNextToken() == modifier) + { + refKind = RefKind.RefReadOnlyParameter; + } + break; } } @@ -962,7 +980,7 @@ internal static RefKind GetModifiers(SyntaxTokenList modifiers, out SyntaxToken internal static ImmutableArray ConditionallyCreateInModifiers(RefKind refKind, bool addRefReadOnlyModifier, Binder binder, BindingDiagnosticBag diagnostics, SyntaxNode syntax) { - if (addRefReadOnlyModifier && refKind == RefKind.In) + if (addRefReadOnlyModifier && refKind is RefKind.In or RefKind.RefReadOnlyParameter) { return CreateInModifiers(binder, diagnostics, syntax); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index e8f76dd887aea..703fd450ca369 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -802,6 +802,7 @@ protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttribut else if (ReportExplicitUseOfReservedAttributes(in arguments, ReservedAttributes.DynamicAttribute | ReservedAttributes.IsReadOnlyAttribute | + // PROTOTYPE: RequiresLocationAttribute ReservedAttributes.IsUnmanagedAttribute | ReservedAttributes.IsByRefLikeAttribute | ReservedAttributes.TupleElementNamesAttribute | diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs index 00e94036b411f..3fcfb09b25f87 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs @@ -86,7 +86,7 @@ internal sealed override void AfterAddingTypeMembersChecks(ConversionsBase conve base.AfterAddingTypeMembersChecks(conversions, diagnostics); var compilation = DeclaringCompilation; - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, diagnostics, modifyCompilation: true); ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); ParameterHelpers.EnsureScopedRefAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index 804ee3eea2b57..327f224f5021d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -314,7 +314,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); } - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, diagnostics, modifyCompilation: true); if (compilation.ShouldEmitNativeIntegerAttributes(ReturnType)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 7cd2ddded6a85..cc31bb64145e6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -24,10 +24,10 @@ protected struct Flags { // We currently pack everything into a 32 bit int with the following layout: // - // | |a|b|e|n|vvv|yy|s|r|q|z|kk|wwwww| + // | |a|b|e|n|vvv|yy|s|r|q|z|kkk|wwwww| // // w = method kind. 5 bits. - // k = ref kind. 2 bits. + // k = ref kind. 3 bits. // z = isExtensionMethod. 1 bit. // q = isMetadataVirtualIgnoringInterfaceChanges. 1 bit. // r = isMetadataVirtual. 1 bit. (At least as true as isMetadataVirtualIgnoringInterfaceChanges.) @@ -45,7 +45,7 @@ protected struct Flags private const int MethodKindMask = (1 << MethodKindSize) - 1; private const int RefKindOffset = MethodKindOffset + MethodKindSize; - private const int RefKindSize = 2; + private const int RefKindSize = 3; private const int RefKindMask = (1 << RefKindSize) - 1; private const int IsExtensionMethodOffset = RefKindOffset + RefKindSize; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs index d52af621917fc..dd415c4bc9d55 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs @@ -250,7 +250,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, compilation.EnsureIsReadOnlyAttributeExists(diagnostics, getReturnTypeLocation(), modifyCompilation: true); } - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, diagnostics, modifyCompilation: true); if (compilation.ShouldEmitNativeIntegerAttributes(ReturnType)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index b279421723cef..98545103a7833 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -283,7 +283,7 @@ public override bool IsImplicitlyDeclared } } - internal override bool IsMetadataIn => RefKind == RefKind.In; + internal override bool IsMetadataIn => RefKind is RefKind.In or RefKind.RefReadOnlyParameter; internal override bool IsMetadataOut => RefKind == RefKind.Out; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs index 06a2bce48b951..b7bc4aa3d7758 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs @@ -109,9 +109,14 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r compilation.SynthesizeTupleNamesAttribute(type.Type)); } - if (this.RefKind == RefKind.RefReadOnly) + switch (this.RefKind) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); + case RefKind.In: + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); + break; + case RefKind.RefReadOnlyParameter: + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeRequiresLocationAttribute(this)); + break; } if (compilation.ShouldEmitNullableAttributes(this)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index 90a4db7315965..edfe4c4005ce0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -816,7 +816,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); } - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, diagnostics, modifyCompilation: true); if (compilation.ShouldEmitNativeIntegerAttributes(Type)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index a95e0fcb29572..107d85c6ff1c4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -469,7 +469,7 @@ string name { } - internal override bool IsMetadataIn => RefKind == RefKind.In; + internal override bool IsMetadataIn => RefKind is RefKind.In or RefKind.RefReadOnlyParameter; internal override bool IsMetadataOut => RefKind == RefKind.Out; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index 7a15d2be2e7f1..13aa1e671f5d1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -176,9 +176,14 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, GetNullableContextValue(), type)); } - if (this.RefKind == RefKind.RefReadOnly) + switch (this.RefKind) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); + case RefKind.In: + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); + break; + case RefKind.RefReadOnlyParameter: + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeRequiresLocationAttribute(this)); + break; } if (this.HasUnscopedRefAttribute && this.ContainingSymbol is SynthesizedDelegateInvokeMethod) @@ -228,7 +233,7 @@ private SynthesizedParameterSymbol( { } - internal sealed override bool IsMetadataIn => RefKind == RefKind.In; + internal sealed override bool IsMetadataIn => RefKind is RefKind.In or RefKind.RefReadOnlyParameter; internal sealed override bool IsMetadataOut => RefKind == RefKind.Out; @@ -381,7 +386,7 @@ internal override bool IsCallerMemberName get => _baseParameterForAttributes?.IsCallerMemberName ?? false; } - internal override bool IsMetadataIn => RefKind == RefKind.In || _baseParameterForAttributes?.GetDecodedWellKnownAttributeData()?.HasInAttribute == true; + internal override bool IsMetadataIn => RefKind is RefKind.In or RefKind.RefReadOnlyParameter || _baseParameterForAttributes?.GetDecodedWellKnownAttributeData()?.HasInAttribute == true; internal override bool IsMetadataOut => RefKind == RefKind.Out || _baseParameterForAttributes?.GetDecodedWellKnownAttributeData()?.HasOutAttribute == true; diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenRefReadonlyParameterTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenRefReadonlyParameterTests.cs new file mode 100644 index 0000000000000..f728ad07c5ee1 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenRefReadonlyParameterTests.cs @@ -0,0 +1,587 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics; + +// PROTOTYPE: Move to RefReadonlyParameterTests.cs. +public partial class RefReadonlyParameterTests : CSharpTestBase +{ + private const string RequiresLocationAttributeName = "RequiresLocationAttribute"; + private const string RequiresLocationAttributeNamespace = "System.Runtime.CompilerServices"; + private const string RequiresLocationAttributeQualifiedName = $"{RequiresLocationAttributeNamespace}.{RequiresLocationAttributeName}"; + + private static void VerifyRequiresLocationAttributeSynthesized(ModuleSymbol module) + { + var attributeType = module.GlobalNamespace.GetMember(RequiresLocationAttributeQualifiedName); + if (module is SourceModuleSymbol) + { + Assert.Null(attributeType); + } + else + { + Assert.NotNull(attributeType); + } + } + + private static void VerifyRefReadonlyParameter(ParameterSymbol parameter, + bool refKind = true, + bool metadataIn = true, + bool attributes = true, + bool modreq = false, + bool useSiteError = false) + { + Assert.Equal(refKind, RefKind.RefReadOnlyParameter == parameter.RefKind); + + Assert.Equal(metadataIn, parameter.IsMetadataIn); + + if (attributes) + { + if (parameter.ContainingModule is SourceModuleSymbol) + { + Assert.Empty(parameter.GetAttributes()); + } + else + { + var attribute = Assert.Single(parameter.GetAttributes()); + Assert.Equal("System.Runtime.CompilerServices.RequiresLocationAttribute", attribute.AttributeClass.ToTestDisplayString()); + Assert.Empty(attribute.ConstructorArguments); + Assert.Empty(attribute.NamedArguments); + } + } + + if (modreq) + { + var mod = Assert.Single(parameter.RefCustomModifiers); + Assert.Equal("System.Runtime.InteropServices.InAttribute", mod.Modifier.ToTestDisplayString()); + } + else + { + Assert.Empty(parameter.RefCustomModifiers); + } + + var method = (MethodSymbol)parameter.ContainingSymbol; + + if (useSiteError) + { + Assert.True(method.HasUnsupportedMetadata); + Assert.True(method.HasUseSiteError); + Assert.Equal((int)ErrorCode.ERR_BindToBogus, method.GetUseSiteDiagnostic().Code); + } + else + { + Assert.False(method.HasUnsupportedMetadata); + Assert.False(method.HasUseSiteError); + } + } + + [Fact] + public void Method() + { + var source = """ + class C + { + public void M(ref readonly int p) { } + } + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var p = m.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + } + + [Fact] + public void ManuallyDefinedAttribute() + { + var source = $$""" + class C + { + public void M(ref readonly int p) { } + } + + namespace {{RequiresLocationAttributeNamespace}} + { + class {{RequiresLocationAttributeName}} : System.Attribute + { + } + } + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + var attribute = m.GlobalNamespace.GetMember(RequiresLocationAttributeQualifiedName); + Assert.NotNull(attribute); + + var p = m.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p); + + if (m is not SourceModuleSymbol) + { + Assert.Same(attribute, p.GetAttributes().Single().AttributeClass); + } + } + } + + [Fact] + public void BothAttributes() + { + // public class C + // { + // public void M([IsReadOnly] ref readonly int p) { } + // } + var ilSource = """ + .class public auto ansi abstract sealed beforefieldinit C extends System.Object + { + .method public hidebysig instance void M([in] int32& p) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void System.Runtime.CompilerServices.RequiresLocationAttribute::.ctor() = ( + 01 00 00 00 + ) + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.RequiresLocationAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + """; + var comp = CreateCompilationWithIL("", ilSource).VerifyDiagnostics(); + + var p = comp.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p, attributes: false); + var attributes = p.GetAttributes(); + Assert.Equal(new[] + { + "System.Runtime.CompilerServices.IsReadOnlyAttribute", + "System.Runtime.CompilerServices.RequiresLocationAttribute" + }, attributes.Select(a => a.AttributeClass.ToTestDisplayString())); + Assert.All(attributes, a => + { + Assert.Empty(a.ConstructorArguments); + Assert.Empty(a.NamedArguments); + }); + } + + [Fact] + public void ReturnParameter() + { + // public class C + // { + // [return: RequiresLocation] + // public ref int M() { } + // } + var ilSource = """ + .class public auto ansi abstract sealed beforefieldinit C extends System.Object + { + .method public hidebysig instance int32& M() cil managed + { + .param [0] + .custom instance void System.Runtime.CompilerServices.RequiresLocationAttribute::.ctor() = ( + 01 00 00 00 + ) + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.RequiresLocationAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + """; + var comp = CreateCompilationWithIL("", ilSource).VerifyDiagnostics(); + + var m = comp.GlobalNamespace.GetMember("C.M"); + Assert.Equal(RefKind.Ref, m.RefKind); + } + + [Fact] + public void Modreq_NonVirtual() + { + // public class C + // { + // public void M(modreq(In) ref readonly int p) { } + // } + var ilSource = """ + .class public auto ansi abstract sealed beforefieldinit C extends System.Object + { + .method public hidebysig instance void M( + [in] int32& modreq(System.Runtime.InteropServices.InAttribute) p + ) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.RequiresLocationAttribute::.ctor() = ( + 01 00 00 00 + ) + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.RequiresLocationAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.InteropServices.InAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + """; + var comp = CreateCompilationWithIL("", ilSource).VerifyDiagnostics(); + + var p = comp.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p, modreq: true, useSiteError: true); + } + + [Fact] + public void Method_Virtual() + { + var source = """ + class C + { + public virtual void M(ref readonly int p) { } + } + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var p = m.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p, modreq: true); + } + } + + [Fact] + public void Method_Abstract() + { + var source = """ + abstract class C + { + public abstract void M(ref readonly int p); + } + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var p = m.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p, modreq: true); + } + } + + [Fact] + public void Constructor() + { + var source = """ + class C + { + public C(ref readonly int p) { } + } + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var p = m.GlobalNamespace.GetMember("C..ctor").Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + } + + [Fact] + public void PrimaryConstructor_Class() + { + var source = """ + class C(ref readonly int p); + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics( + // (1,26): warning CS9113: Parameter 'p' is unread. + // class C(ref readonly int p); + Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "p").WithArguments("p").WithLocation(1, 26)); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var p = m.GlobalNamespace.GetMember("C..ctor").Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + } + + [Fact] + public void PrimaryConstructor_Struct() + { + var source = """ + struct C(ref readonly int p); + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics( + // (1,27): warning CS9113: Parameter 'p' is unread. + // struct C(ref readonly int p); + Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "p").WithArguments("p").WithLocation(1, 27)); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var c = m.GlobalNamespace.GetTypeMember("C"); + var ctor = c.InstanceConstructors.Single(s => s.Parameters is [{ Name: "p" }]); + var p = ctor.Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + } + + [Fact] + public void PrimaryConstructor_Record() + { + var source = """ + record C(ref readonly int p); + """; + var verifier = CompileAndVerify(new[] { source, IsExternalInitTypeDefinition }, + sourceSymbolValidator: verify, symbolValidator: verify, + verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var c = m.GlobalNamespace.GetTypeMember("C"); + var ctor = c.InstanceConstructors.Single(s => s.Parameters is [{ Name: "p" }]); + var p = ctor.Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + } + + [Fact] + public void PrimaryConstructor_RecordStruct() + { + var source = """ + record struct C(ref readonly int p); + """; + var verifier = CompileAndVerify(new[] { source, IsExternalInitTypeDefinition }, + sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var c = m.GlobalNamespace.GetTypeMember("C"); + var ctor = c.InstanceConstructors.Single(s => s.Parameters is [{ Name: "p" }]); + var p = ctor.Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + } + + [Fact] + public void Delegate() + { + var source = """ + delegate void D(ref readonly int p); + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + var p = m.GlobalNamespace.GetMember("D.Invoke").Parameters.Single(); + VerifyRefReadonlyParameter(p, modreq: true); + } + } + + [Fact] + public void Lambda() + { + var source = """ + var lam = (ref readonly int p) => { }; + System.Console.WriteLine(lam.GetType()); + """; + var verifier = CompileAndVerify(source, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + sourceSymbolValidator: verify, symbolValidator: verify, + expectedOutput: "<>f__AnonymousDelegate0`1[System.Int32]"); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + if (m is not SourceModuleSymbol) + { + var p = m.GlobalNamespace.GetMember("Program.<>c.<
$>b__0_0").Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + } + } + + [Fact] + public void LocalFunction() + { + var source = """ + void local(ref readonly int p) { } + System.Console.WriteLine(((object)local).GetType()); + """; + var verifier = CompileAndVerify(source, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + sourceSymbolValidator: verify, symbolValidator: verify, + expectedOutput: "<>f__AnonymousDelegate0`1[System.Int32]"); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + if (m is not SourceModuleSymbol) + { + var p = m.GlobalNamespace.GetMember("Program.<
$>g__local|0_0").Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + } + } + + [Theory] + [InlineData("var x = (ref readonly int p) => { };")] + [InlineData("var x = local; void local(ref readonly int p) { }")] + public void AnonymousDelegate(string def) + { + var source = $""" + {def} + System.Console.WriteLine(((object)x).GetType()); + """; + var verifier = CompileAndVerify(source, sourceSymbolValidator: verify, symbolValidator: verify, + expectedOutput: "<>f__AnonymousDelegate0`1[System.Int32]"); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + VerifyRequiresLocationAttributeSynthesized(m); + + if (m is not SourceModuleSymbol) + { + var p = m.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke").Parameters.Single(); + VerifyRefReadonlyParameter(p, + // PROTOTYPE: Invoke method is virtual but no modreq is emitted. This happens for `in` parameters, as well. + useSiteError: true); + } + } + } + + [Fact] + public void FunctionPointer() + { + var source = """ + class C + { + public unsafe void M(delegate* p) { } + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.UnsafeReleaseDll, + sourceSymbolValidator: verify, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol m) + { + Assert.Null(m.GlobalNamespace.GetMember(RequiresLocationAttributeQualifiedName)); + + var p = m.GlobalNamespace.GetMember("C.M").Parameters.Single(); + var ptr = (FunctionPointerTypeSymbol)p.Type; + var p2 = ptr.Signature.Parameters.Single(); + VerifyRefReadonlyParameter(p2, refKind: m is SourceModuleSymbol, modreq: true, attributes: false); + Assert.Equal(m is SourceModuleSymbol ? RefKind.RefReadOnlyParameter : RefKind.In, p2.RefKind); + Assert.Empty(p2.GetAttributes()); + } + } + + [Fact] + public void AttributeIL() + { + var source = """ + class C + { + public void M(ref readonly int p) { } + } + """; + var verifier = CompileAndVerify(source, targetFramework: TargetFramework.NetStandard20); + verifier.VerifyDiagnostics(); + verifier.VerifyTypeIL(RequiresLocationAttributeName, """ + .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.RequiresLocationAttribute + extends [netstandard]System.Attribute + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2050 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [netstandard]System.Attribute::.ctor() + IL_0006: ret + } // end of method RequiresLocationAttribute::.ctor + } // end of class System.Runtime.CompilerServices.RequiresLocationAttribute + """); + } +} diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/RefReadonlyParameterTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/RefReadonlyParameterTests.cs index 7b42b201dd414..16200efcc2c4b 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/RefReadonlyParameterTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/RefReadonlyParameterTests.cs @@ -2,12 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics; -public class RefReadonlyParameterTests : CSharpTestBase +public partial class RefReadonlyParameterTests : CSharpTestBase { [Fact] public void Modifier() @@ -24,7 +27,51 @@ class C Diagnostic(ErrorCode.ERR_FeatureInPreview, "readonly").WithArguments("ref readonly parameters").WithLocation(3, 16)); CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); - CreateCompilation(source).VerifyDiagnostics(); + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var p = comp.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p); + } + + [Fact] + public void Modifier_Invalid_01() + { + var source = """ + class C + { + void M(ref params readonly int[] p) => throw null; + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (3,16): error CS8328: The parameter modifier 'params' cannot be used with 'ref' + // void M(ref params readonly int[] p) => throw null; + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "params").WithArguments("params", "ref").WithLocation(3, 16), + // (3,23): error CS9501: 'readonly' modifier must be specified after 'ref'. + // void M(ref params readonly int[] p) => throw null; + Diagnostic(ErrorCode.ERR_RefReadOnlyWrongOrdering, "readonly").WithLocation(3, 23)); + + var p = comp.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p, refKind: false, metadataIn: false); + Assert.Equal(RefKind.Ref, p.RefKind); + } + + [Fact] + public void Modifier_Invalid_02() + { + var source = """ + class C + { + void M(in readonly int p) => throw null; + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (3,15): error CS9501: 'readonly' modifier must be specified after 'ref'. + // void M(in readonly int p) => throw null; + Diagnostic(ErrorCode.ERR_RefReadOnlyWrongOrdering, "readonly").WithLocation(3, 15)); + + var p = comp.GlobalNamespace.GetMember("C.M").Parameters.Single(); + VerifyRefReadonlyParameter(p, refKind: false); + Assert.Equal(RefKind.In, p.RefKind); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs index e53fad5d80fcd..4077e98a3dcc9 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs @@ -1926,7 +1926,7 @@ void M() Assert.Equal("delegate* ptr1", model.GetDeclaredSymbol(decls[0]).ToTestDisplayString()); Assert.Equal("delegate* ptr2", model.GetDeclaredSymbol(decls[1]).ToTestDisplayString()); - Assert.Equal("delegate* ptr3", model.GetDeclaredSymbol(decls[2]).ToTestDisplayString()); + Assert.Equal("delegate* ptr3", model.GetDeclaredSymbol(decls[2]).ToTestDisplayString()); Assert.Equal("delegate* ptr4", model.GetDeclaredSymbol(decls[3]).ToTestDisplayString()); Assert.Equal("delegate* ptr5", model.GetDeclaredSymbol(decls[4]).ToTestDisplayString()); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index f59fea213f982..949ac0809f2ea 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -602,6 +602,7 @@ public void AllWellKnownTypes() case WellKnownType.System_Runtime_CompilerServices_NullableContextAttribute: case WellKnownType.System_Runtime_CompilerServices_NullablePublicOnlyAttribute: case WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute: + case WellKnownType.System_Runtime_CompilerServices_RequiresLocationAttribute: case WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute: case WellKnownType.System_Span_T: case WellKnownType.System_ReadOnlySpan_T: @@ -1002,6 +1003,7 @@ public void AllWellKnownTypeMembers() case WellKnownMember.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor: case WellKnownMember.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute__ctor: case WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__CreateSpanRuntimeFieldHandle: + case WellKnownMember.System_Runtime_CompilerServices_RequiresLocationAttribute__ctor: // Not yet in the platform. continue; case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile: diff --git a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs index 478a6c3f80911..4488e7328c515 100644 --- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs +++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs @@ -1132,6 +1132,11 @@ internal bool HasIsByRefLikeAttribute(EntityHandle token) return FindTargetAttribute(token, AttributeDescription.IsByRefLikeAttribute).HasValue; } + internal bool HasRequiresLocationAttribute(EntityHandle token) + { + return FindTargetAttribute(token, AttributeDescription.RequiresLocationAttribute).HasValue; + } + internal const string ByRefLikeMarker = "Types with embedded references are not supported in this version of your compiler."; internal const string RequiredMembersMarker = "Constructors of types with required members are not supported in this version of your compiler."; diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 6135880dd7fc0..57f2e4bd63ec4 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -11,6 +11,7 @@ Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers.GetAnalyzerSyntaxDia Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers.GetAnalyzerSyntaxDiagnosticsAsync(Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Text.TextSpan? filterSpan, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext.FilterSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan? Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.FilterSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan? +Microsoft.CodeAnalysis.RefKind.RefReadOnlyParameter = 5 -> Microsoft.CodeAnalysis.RefKind static Microsoft.CodeAnalysis.SeparatedSyntaxList.explicit operator Microsoft.CodeAnalysis.SeparatedSyntaxList(Microsoft.CodeAnalysis.SeparatedSyntaxList nodes) -> Microsoft.CodeAnalysis.SeparatedSyntaxList static Microsoft.CodeAnalysis.SeparatedSyntaxList.op_Implicit(Microsoft.CodeAnalysis.SeparatedSyntaxList nodes) -> Microsoft.CodeAnalysis.SeparatedSyntaxList static Microsoft.CodeAnalysis.SyntaxList.explicit operator Microsoft.CodeAnalysis.SyntaxList(Microsoft.CodeAnalysis.SyntaxList nodes) -> Microsoft.CodeAnalysis.SyntaxList diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index 59fe257f622ad..f961415d03941 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -399,6 +399,7 @@ static AttributeDescription() internal static readonly AttributeDescription InAttribute = new AttributeDescription("System.Runtime.InteropServices", "InAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription OutAttribute = new AttributeDescription("System.Runtime.InteropServices", "OutAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription IsReadOnlyAttribute = new AttributeDescription("System.Runtime.CompilerServices", "IsReadOnlyAttribute", s_signatures_HasThis_Void_Only); + internal static readonly AttributeDescription RequiresLocationAttribute = new AttributeDescription("System.Runtime.CompilerServices", "RequiresLocationAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription IsUnmanagedAttribute = new AttributeDescription("System.Runtime.CompilerServices", "IsUnmanagedAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription CoClassAttribute = new AttributeDescription("System.Runtime.InteropServices", "CoClassAttribute", s_signatures_HasThis_Void_Type_Only); internal static readonly AttributeDescription GuidAttribute = new AttributeDescription("System.Runtime.InteropServices", "GuidAttribute", s_signatures_HasThis_Void_String_Only); diff --git a/src/Compilers/Core/Portable/Symbols/RefKind.cs b/src/Compilers/Core/Portable/Symbols/RefKind.cs index 81e6f18f96660..e1a41298596d9 100644 --- a/src/Compilers/Core/Portable/Symbols/RefKind.cs +++ b/src/Compilers/Core/Portable/Symbols/RefKind.cs @@ -40,6 +40,11 @@ public enum RefKind : byte // NOTE: There is an additional value of this enum type - RefKindExtensions.StrictIn == RefKind.In + 1 // It is used internally during lowering. // Consider that when adding values or changing this enum in some other way. + + /// + /// Indicates a "ref readonly" parameter. + /// + RefReadOnlyParameter = 5, // PROTOTYPE: Change to 4 to make public values sequential. } internal static class RefKindExtensions @@ -51,6 +56,7 @@ internal static string ToParameterDisplayString(this RefKind kind) case RefKind.Out: return "out"; case RefKind.Ref: return "ref"; case RefKind.In: return "in"; + case RefKind.RefReadOnlyParameter: return "ref readonly"; default: throw ExceptionUtilities.UnexpectedValue(kind); } } @@ -73,6 +79,7 @@ internal static string ToParameterPrefix(this RefKind kind) case RefKind.Out: return "out "; case RefKind.Ref: return "ref "; case RefKind.In: return "in "; + case RefKind.RefReadOnlyParameter: return "ref readonly "; case RefKind.None: return string.Empty; default: throw ExceptionUtilities.UnexpectedValue(kind); } diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 43233e92bdb3e..8eed831ae96ba 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -474,6 +474,7 @@ internal enum WellKnownMember System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor, System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor, + System_Runtime_CompilerServices_RequiresLocationAttribute__ctor, System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor, System_ObsoleteAttribute__ctor, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 2b102b4483f52..47c357941690a 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -3295,6 +3295,13 @@ static WellKnownMembers() 0, // Arity 0, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + + // System_Runtime_CompilerServices_RequiresLocationAttribute__ctor + (byte)(MemberFlags.Constructor), // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_RequiresLocationAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type // System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor (byte)(MemberFlags.Constructor), // Flags @@ -4389,6 +4396,7 @@ static WellKnownMembers() ".ctor", // System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_ReferenceAssemblyAttribute__ctor ".ctor", // System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor + ".ctor", // System_Runtime_CompilerServices_RequiresLocationAttribute__ctor ".ctor", // System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor ".ctor", // System_Runtime_CompilerServices_ObsoleteAttribute__ctor ".ctor", // System_Span_T__ctor_Pointer diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 25eaebfc06e49..fbb348359e009 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -272,6 +272,7 @@ internal enum WellKnownType System_Runtime_CompilerServices_ReferenceAssemblyAttribute, System_Runtime_CompilerServices_IsReadOnlyAttribute, + System_Runtime_CompilerServices_RequiresLocationAttribute, System_Runtime_CompilerServices_IsByRefLikeAttribute, System_Runtime_InteropServices_InAttribute, System_ObsoleteAttribute, @@ -596,6 +597,7 @@ internal static class WellKnownTypes "System.Runtime.CompilerServices.ReferenceAssemblyAttribute", "System.Runtime.CompilerServices.IsReadOnlyAttribute", + "System.Runtime.CompilerServices.RequiresLocationAttribute", "System.Runtime.CompilerServices.IsByRefLikeAttribute", "System.Runtime.InteropServices.InAttribute", "System.ObsoleteAttribute", diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb index 1507b25725575..758fdc0e43844 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb @@ -5285,6 +5285,34 @@ End Class SymbolDisplayPartKind.Keyword) End Sub + + Public Sub RefReadonlyParameter() + Dim source = +"public class C +{ + public void M(ref readonly int p) { } +}" + Dim parseOptions = CSharp.CSharpParseOptions.Default.WithLanguageVersion(CSharp.LanguageVersion.Preview) + Dim comp = CreateCSharpCompilation(source, parseOptions).VerifyDiagnostics() + Dim m = comp.GlobalNamespace.GetTypeMembers("C").Single().GetMembers("M").Single() + ' Ref modifiers are not included: https://github.com/dotnet/roslyn/issues/14683 + Dim format = SymbolDisplayFormat.VisualBasicErrorMessageFormat. + AddParameterOptions(SymbolDisplayParameterOptions.IncludeParamsRefOut) + Verify(ToDisplayParts(m, format), "Public Sub M(p As Integer)", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.MethodName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation) + End Sub + ' SymbolDisplayMemberOptions.IncludeRef is ignored in VB. diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 9a21cce528e3f..1e2fde7cac2e2 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -555,7 +555,8 @@ End Namespace WellKnownType.System_Runtime_CompilerServices_RefSafetyRulesAttribute, WellKnownType.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute, WellKnownType.System_MemoryExtensions, - WellKnownType.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute + WellKnownType.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute, + WellKnownType.System_Runtime_CompilerServices_RequiresLocationAttribute ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel @@ -630,7 +631,8 @@ End Namespace WellKnownType.System_Runtime_CompilerServices_ScopedRefAttribute, WellKnownType.System_Runtime_CompilerServices_RefSafetyRulesAttribute, WellKnownType.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute, - WellKnownType.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute + WellKnownType.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute, + WellKnownType.System_Runtime_CompilerServices_RequiresLocationAttribute ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel @@ -731,7 +733,8 @@ End Namespace WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, WellKnownMember.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor, WellKnownMember.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute__ctor, - WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__CreateSpanRuntimeFieldHandle + WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__CreateSpanRuntimeFieldHandle, + WellKnownMember.System_Runtime_CompilerServices_RequiresLocationAttribute__ctor ' Not available yet, but will be in upcoming release. Continue For Case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, @@ -920,7 +923,8 @@ End Namespace WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, WellKnownMember.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor, WellKnownMember.System_Runtime_CompilerServices_MetadataUpdateOriginalTypeAttribute__ctor, - WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__CreateSpanRuntimeFieldHandle + WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__CreateSpanRuntimeFieldHandle, + WellKnownMember.System_Runtime_CompilerServices_RequiresLocationAttribute__ctor ' Not available yet, but will be in upcoming release. Continue For Case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, diff --git a/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.ParameterViewModels.cs b/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.ParameterViewModels.cs index 07bef11bebee8..1d0a049bd6d55 100644 --- a/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.ParameterViewModels.cs +++ b/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.ParameterViewModels.cs @@ -178,14 +178,14 @@ public override string Modifier switch (ParameterSymbol.Language) { case LanguageNames.CSharp: - return ModifierText("out", "ref", "in", "params", "this"); + return ModifierText("out", "ref", "ref readonly", "in", "params", "this"); case LanguageNames.VisualBasic: - return ModifierText(@out: null, "ByRef", @in: null, "ParamArray", "Me"); + return ModifierText(@out: null, "ByRef", refReadonly: null, @in: null, "ParamArray", "Me"); default: return string.Empty; } - string ModifierText(string? @out, string? @ref, string? @in, string? @params, string? @this) + string ModifierText(string? @out, string? @ref, string? refReadonly, string? @in, string? @params, string? @this) { switch (ParameterSymbol.RefKind) { @@ -195,6 +195,8 @@ string ModifierText(string? @out, string? @ref, string? @in, string? @params, st return @ref ?? string.Empty; case RefKind.In: return @in ?? string.Empty; + case RefKind.RefReadOnlyParameter: + return refReadonly ?? string.Empty; } if (ParameterSymbol.IsParams) diff --git a/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb b/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb index bdc903c9b352a..3766e23d7039c 100644 --- a/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb +++ b/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb @@ -295,22 +295,23 @@ class MyClass Public Async Function TestRefKindsDisplayedCorrectly() As Tasks.Task - Dim includedInTest = {RefKind.None, RefKind.Ref, RefKind.Out, RefKind.In, RefKind.RefReadOnly} + Dim includedInTest = {RefKind.None, RefKind.Ref, RefKind.Out, RefKind.In, RefKind.RefReadOnly, RefKind.RefReadOnlyParameter} Assert.Equal(includedInTest, EnumUtilities.GetValues(Of RefKind)()) Dim markup = Dim state = Await GetViewModelTestStateAsync(markup, LanguageNames.CSharp) - VerifyOpeningState(state.ViewModel, "private void Method(int p1, ref int p2, in int p3, out int p4)") + VerifyOpeningState(state.ViewModel, "private void Method(int p1, ref int p2, in int p3, out int p4, ref readonly int p5)") Assert.Equal("", state.ViewModel.AllParameters(0).Modifier) Assert.Equal("ref", state.ViewModel.AllParameters(1).Modifier) Assert.Equal("in", state.ViewModel.AllParameters(2).Modifier) Assert.Equal("out", state.ViewModel.AllParameters(3).Modifier) + Assert.Equal("ref readonly", state.ViewModel.AllParameters(4).Modifier) End Function