From 7ab83bb21d1b1130fc3b64986543251e494c65f6 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Tue, 2 Aug 2022 21:25:32 -0700 Subject: [PATCH] Support UnscopedRefAttribute (#62783) --- .../Compiler Breaking Changes - DotNet 7.md | 25 +- .../Portable/Binder/Binder.ValueChecks.cs | 30 +- .../Portable/Binder/Binder_Expressions.cs | 4 +- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../CSharp/Portable/Errors/ErrorFacts.cs | 1 + .../SynthesizedStateMachineProperty.cs | 2 + .../Lowering/SynthesizedMethodBaseSymbol.cs | 2 +- .../SymbolDisplayVisitor.Members.cs | 6 +- .../AnonymousType.PropertySymbol.cs | 2 + .../MethodWellKnownAttributeData.cs | 16 + .../ParameterEarlyWellKnownAttributeData.cs | 15 + .../PropertyWellKnownAttributeData.cs | 16 + .../Portable/Symbols/ErrorMethodSymbol.cs | 2 + .../Portable/Symbols/ErrorPropertySymbol.cs | 2 + .../FunctionPointerMethodSymbol.cs | 1 + .../FunctionPointerParameterSymbol.cs | 3 +- .../Symbols/Metadata/PE/PEMethodSymbol.cs | 33 +- .../Symbols/Metadata/PE/PEParameterSymbol.cs | 10 +- .../Symbols/Metadata/PE/PEPropertySymbol.cs | 38 +- .../CSharp/Portable/Symbols/MethodSymbol.cs | 2 + .../Symbols/MethodSymbolExtensions.cs | 8 + .../Portable/Symbols/ParameterSymbol.cs | 12 +- .../CSharp/Portable/Symbols/PropertySymbol.cs | 2 + .../Symbols/ReducedExtensionMethodSymbol.cs | 2 + .../Retargeting/RetargetingMethodSymbol.cs | 14 + .../Symbols/SignatureOnlyMethodSymbol.cs | 2 + .../Symbols/SignatureOnlyParameterSymbol.cs | 3 +- .../Symbols/SignatureOnlyPropertySymbol.cs | 2 + .../Symbols/Source/ParameterHelpers.cs | 4 +- .../Source/SourceClonedParameterSymbol.cs | 3 +- .../Source/SourceComplexParameterSymbol.cs | 44 + ...berContainerSymbol_ImplementationChecks.cs | 2 +- .../SourceMethodSymbolWithAttributes.cs | 13 + .../Symbols/Source/SourceParameterSymbol.cs | 8 +- .../Source/SourceParameterSymbolBase.cs | 2 +- .../Source/SourcePropertySymbolBase.cs | 26 + .../Source/SourceSimpleParameterSymbol.cs | 2 + .../Symbols/Source/ThisParameterSymbol.cs | 33 +- .../SynthesizedEntryPointSymbol.cs | 2 + .../SynthesizedGlobalMethodSymbol.cs | 2 + .../SynthesizedInstanceMethodSymbol.cs | 2 + .../SynthesizedIntrinsicOperatorSymbol.cs | 2 + .../Synthesized/SynthesizedParameterSymbol.cs | 8 +- .../SynthesizedStaticConstructor.cs | 2 + .../Symbols/Wrapped/WrappedMethodSymbol.cs | 2 + .../Symbols/Wrapped/WrappedParameterSymbol.cs | 4 +- .../Symbols/Wrapped/WrappedPropertySymbol.cs | 2 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../AttributeTests_CallerInfoAttributes.cs | 22 + .../AttributeTests_LifetimeAnnotation.cs | 8 +- .../Test/Semantic/Semantics/RefFieldTests.cs | 1473 ++++++++++++++++- .../SymbolDisplay/SymbolDisplayTests.cs | 26 + .../Symbol/Symbols/MissingSpecialMember.cs | 2 + .../Core/Portable/MetadataReader/PEModule.cs | 5 + .../Attributes/AttributeDescription.cs | 1 + .../Core/Portable/WellKnownMember.cs | 1 + .../Core/Portable/WellKnownMembers.cs | 8 + src/Compilers/Core/Portable/WellKnownTypes.cs | 2 + .../Test/Utilities/CSharp/CSharpTestBase.cs | 9 + .../WellKnownTypeValidationTests.vb | 10 +- .../Symbols/EEMethodSymbol.cs | 2 + .../Symbols/ObjectIdLocalSymbol.cs | 2 +- .../Symbols/PlaceholderMethodSymbol.cs | 2 + 76 files changed, 2012 insertions(+), 48 deletions(-) diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index 8df4598162ef7..52e464cce567d 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -128,15 +128,24 @@ static ref T ReturnOutParamByRef(out T t) } ``` -A possible workaround is to change the method signature to pass the parameter by `ref` instead. +Possible workarounds are: +1. Use `System.Diagnostics.CodeAnalysis.UnscopedRefAttribute` to mark the reference as unscoped. + ```csharp + static ref T ReturnOutParamByRef([UnscopedRef] out T t) + { + t = default; + return ref t; // ok + } + ``` -```csharp -static ref T ReturnRefParamByRef(ref T t) -{ - t = default; - return ref t; // ok -} -``` +1. Change the method signature to pass the parameter by `ref`. + ```csharp + static ref T ReturnRefParamByRef(ref T t) + { + t = default; + return ref t; // ok + } + ``` ## Method ref struct return escape analysis depends on ref escape of ref arguments diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 49db2a7a7cb27..728cbdc1751ba 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -782,7 +782,7 @@ private uint GetParameterValEscape(ParameterSymbol parameter) { if (UseUpdatedEscapeRules) { - return parameter.Scope == DeclarationScope.ValueScoped ? + return parameter.EffectiveScope == DeclarationScope.ValueScoped ? Binder.TopLevelScope : Binder.ExternalScope; } @@ -796,7 +796,7 @@ private uint GetParameterRefEscape(ParameterSymbol parameter) { if (UseUpdatedEscapeRules) { - return parameter.RefKind is RefKind.None || parameter.Scope != DeclarationScope.Unscoped ? Binder.TopLevelScope : Binder.ExternalScope; + return parameter.RefKind is RefKind.None || parameter.EffectiveScope != DeclarationScope.Unscoped ? Binder.TopLevelScope : Binder.ExternalScope; } else { @@ -1822,7 +1822,7 @@ bool considerParameter(ParameterSymbol? parameter) // SPEC: For a given argument `a` that is passed to parameter `p`: // SPEC: 1. ... // SPEC: 2. If `p` is `scoped` then `a` does not contribute *safe-to-escape* when considering arguments. - if (parameter?.Scope == DeclarationScope.ValueScoped) + if (parameter?.EffectiveScope == DeclarationScope.ValueScoped) { return false; } @@ -1909,7 +1909,7 @@ private static RefKind GetEffectiveRefKind( } } - scope = paramIndex >= 0 ? parameters[paramIndex].Scope : DeclarationScope.Unscoped; + scope = paramIndex >= 0 ? parameters[paramIndex].EffectiveScope : DeclarationScope.Unscoped; return effectiveRefKind; } @@ -2227,6 +2227,8 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return ((BoundLocal)expr).LocalSymbol.RefEscapeScope; case BoundKind.ThisReference: + Debug.Assert(this.ContainingMember() is MethodSymbol { ThisParameter: not null }); + var thisref = (BoundThisReference)expr; // "this" is an RValue, unless in a struct. @@ -2235,6 +2237,15 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres break; } + if (UseUpdatedEscapeRules) + { + if (this.ContainingMember() is MethodSymbol { ThisParameter: var thisParameter } && + thisParameter.EffectiveScope == DeclarationScope.Unscoped) + { + return Binder.ExternalScope; + } + } + //"this" is not returnable by reference in a struct. // can ref escape to any other level return Binder.TopLevelScope; @@ -2481,6 +2492,8 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF return CheckLocalRefEscape(node, local, escapeTo, checkingReceiver, diagnostics); case BoundKind.ThisReference: + Debug.Assert(this.ContainingMember() is MethodSymbol { ThisParameter: not null }); + var thisref = (BoundThisReference)expr; // "this" is an RValue, unless in a struct. @@ -2492,6 +2505,15 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF //"this" is not returnable by reference in a struct. if (escapeTo == Binder.ExternalScope) { + if (UseUpdatedEscapeRules) + { + if (this.ContainingMember() is MethodSymbol { ThisParameter: var thisParameter } && + thisParameter.EffectiveScope == DeclarationScope.Unscoped) + { + // can ref escape to any other level + return true; + } + } Error(diagnostics, ErrorCode.ERR_RefReturnStructThis, node); return false; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 096834571c6e6..147c0d1229cd1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8846,8 +8846,8 @@ private MethodGroupResolution ResolveDefaultMethodGroup( } var parameters = method.Parameters; - var parameterScopes = parameters.Any(p => p.Scope != DeclarationScope.Unscoped) ? - parameters.SelectAsArray(p => p.Scope) : + var parameterScopes = parameters.Any(p => p.EffectiveScope != DeclarationScope.Unscoped) ? + parameters.SelectAsArray(p => p.EffectiveScope) : default; return GetMethodGroupOrLambdaDelegateType(node.Syntax, method.RefKind, method.ReturnTypeWithAnnotations, method.ParameterRefKinds, parameterScopes, method.ParameterTypesWithAnnotations); } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index f2fbdd8911381..5f47ca4a90620 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7205,4 +7205,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Types and aliases cannot be named 'scoped'. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 05b7a32e2c1ee..40490006d108a 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2111,6 +2111,7 @@ internal enum ErrorCode ERR_CannotMatchOnINumberBase = 9060, ERR_MisplacedScoped = 9061, ERR_ScopedTypeNameDisallowed = 9062, + ERR_UnscopedRefAttributeUnsupportedTarget = 9063, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index a338a2240b4c6..89991a52a8e1d 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2212,6 +2212,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_CannotMatchOnINumberBase: case ErrorCode.ERR_MisplacedScoped: case ErrorCode.ERR_ScopedTypeNameDisallowed: + case ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs index 8ca68abb3e257..aa243c9cf0172 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs @@ -153,6 +153,8 @@ public override bool IsExtern internal override bool IsRequired => false; + internal sealed override bool HasUnscopedRefAttribute => false; + internal override ObsoleteAttributeData ObsoleteAttributeData { get { return null; } diff --git a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs index d7d406f635e22..60f032ab0c80a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs @@ -136,7 +136,7 @@ private ImmutableArray MakeParameters() ordinal++, p.RefKind, p.Name, - p.Scope, + p.DeclaredScope, // the synthesized parameter doesn't need to have the same ref custom modifiers as the base refCustomModifiers: default, inheritAttributes ? p as SourceComplexParameterSymbolBase : null)); diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 870861d9d9d7f..f70c7bcf0e6e2 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -612,7 +612,7 @@ void visitFunctionPointerSignature(IMethodSymbol symbol) foreach (var param in symbol.Parameters) { // https://github.com/dotnet/roslyn/issues/61647: Use public API. - Debug.Assert((param as Symbols.PublicModel.ParameterSymbol)?.GetSymbol().Scope switch + Debug.Assert((param as Symbols.PublicModel.ParameterSymbol)?.GetSymbol().EffectiveScope switch { null => true, DeclarationScope.Unscoped => param.RefKind != RefKind.Out, @@ -780,7 +780,7 @@ public override void VisitParameter(IParameterSymbol symbol) AddCustomModifiersIfNeeded(symbol.RefCustomModifiers, leadingSpace: false, trailingSpace: true); // https://github.com/dotnet/roslyn/issues/61647: Use public API. - if ((symbol as Symbols.PublicModel.ParameterSymbol)?.GetSymbol().Scope == DeclarationScope.ValueScoped && + if ((symbol as Symbols.PublicModel.ParameterSymbol)?.GetSymbol().EffectiveScope == DeclarationScope.ValueScoped && format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) { AddKeyword(SyntaxKind.ScopedKeyword); @@ -1079,7 +1079,7 @@ private void AddParameterRefKindIfNeeded(IParameterSymbol symbol) if (format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeParamsRefOut)) { // https://github.com/dotnet/roslyn/issues/61647: Use public API. - if ((symbol as Symbols.PublicModel.ParameterSymbol)?.GetSymbol().Scope == DeclarationScope.RefScoped && + if ((symbol as Symbols.PublicModel.ParameterSymbol)?.GetSymbol().EffectiveScope == DeclarationScope.RefScoped && symbol.RefKind != RefKind.Out && format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.IncludeScoped)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs index 5d5630d1af30d..7fa7f2c09c182 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs @@ -131,6 +131,8 @@ public override bool IsAbstract internal override bool IsRequired => false; + internal sealed override bool HasUnscopedRefAttribute => false; + internal sealed override ObsoleteAttributeData ObsoleteAttributeData { get { return null; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/MethodWellKnownAttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/MethodWellKnownAttributeData.cs index 5a4c0ad0ea706..e95f295b21e87 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/MethodWellKnownAttributeData.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/MethodWellKnownAttributeData.cs @@ -44,6 +44,22 @@ public bool HasSkipLocalsInitAttribute } } + private bool _hasUnscopedRefAttribute; + public bool HasUnscopedRefAttribute + { + get + { + VerifySealed(expected: true); + return _hasUnscopedRefAttribute; + } + set + { + VerifySealed(expected: false); + _hasUnscopedRefAttribute = value; + SetDataStored(); + } + } + private ImmutableArray _memberNotNullAttributeData = ImmutableArray.Empty; public void AddNotNullMember(string memberName) diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterEarlyWellKnownAttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterEarlyWellKnownAttributeData.cs index 3bcfe343f2c40..317fd4b0754d1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterEarlyWellKnownAttributeData.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterEarlyWellKnownAttributeData.cs @@ -11,5 +11,20 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// internal sealed class ParameterEarlyWellKnownAttributeData : CommonParameterEarlyWellKnownAttributeData { + private bool _hasUnscopedRefAttribute; + public bool HasUnscopedRefAttribute + { + get + { + VerifySealed(expected: true); + return _hasUnscopedRefAttribute; + } + set + { + VerifySealed(expected: false); + _hasUnscopedRefAttribute = value; + SetDataStored(); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/PropertyWellKnownAttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/PropertyWellKnownAttributeData.cs index 99d6a9fdab3bd..8993a1d8ff8f2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/PropertyWellKnownAttributeData.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/PropertyWellKnownAttributeData.cs @@ -94,6 +94,22 @@ public bool HasSkipLocalsInitAttribute } } + private bool _hasUnscopedRefAttribute; + public bool HasUnscopedRefAttribute + { + get + { + VerifySealed(expected: true); + return _hasUnscopedRefAttribute; + } + set + { + VerifySealed(expected: false); + _hasUnscopedRefAttribute = value; + SetDataStored(); + } + } + private ImmutableArray _memberNotNullAttributeData = ImmutableArray.Empty; public void AddNotNullMember(string memberName) diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs index c28022499e6cd..2341272e590ff 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs @@ -284,5 +284,7 @@ protected override bool HasSetsRequiredMembersImpl return false; } } + + internal sealed override bool HasUnscopedRefAttribute => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs index 536658ed92722..e11767c30a2a2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs @@ -79,6 +79,8 @@ public ErrorPropertySymbol(Symbol containingSymbol, TypeSymbol type, string name internal override bool IsRequired => false; + internal sealed override bool HasUnscopedRefAttribute => false; + internal sealed override ObsoleteAttributeData ObsoleteAttributeData { get { return null; } } public override ImmutableArray Parameters { get { return ImmutableArray.Empty; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index 3d33ae01354b3..2054a5e8375d4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -831,5 +831,6 @@ public override bool IsVararg internal override IEnumerable GetSecurityInformation() => throw ExceptionUtilities.Unreachable; internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + internal sealed override bool HasUnscopedRefAttribute => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs index 281f146b53c4e..150a61e2d46d9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs @@ -29,7 +29,8 @@ public FunctionPointerParameterSymbol(TypeWithAnnotations typeWithAnnotations, R public override int Ordinal { get; } public override Symbol ContainingSymbol => _containingSymbol; public override ImmutableArray RefCustomModifiers { get; } - internal override DeclarationScope Scope => RefKind == RefKind.Out ? DeclarationScope.RefScoped : DeclarationScope.Unscoped; + internal override DeclarationScope DeclaredScope => RefKind == RefKind.Out ? DeclarationScope.RefScoped : DeclarationScope.Unscoped; + internal override DeclarationScope EffectiveScope => DeclaredScope; public override bool Equals(Symbol other, TypeCompareKind compareKind) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index 852bf34165db0..4da3ee989e5c5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -47,7 +47,7 @@ private struct PackedFlags { // We currently pack everything into a 32-bit int with the following layout: // - // |w|v|u|t|s|r|q|p|ooo|n|m|l|k|j|i|h|g|f|e|d|c|b|aaaaa| + // |y|x|w|v|u|t|s|r|q|p|ooo|n|m|l|k|j|i|h|g|f|e|d|c|b|aaaaa| // // a = method kind. 5 bits. // b = method kind populated. 1 bit. @@ -74,7 +74,9 @@ private struct PackedFlags // u = IsUnmanagedCallersOnlyAttributePopulated. 1 bit. // v = IsSetsRequiredMembersBit. 1 bit. // w = IsSetsRequiredMembersPopulated. 1 bit. - // 4 bits remain for future purposes. + // x = IsUnscopedRef. 1 bit. + // y = IsUnscopedRefPopulated. 1 bit. + // 2 bits remain for future purposes. private const int MethodKindOffset = 0; private const int MethodKindMask = 0x1F; @@ -102,6 +104,8 @@ private struct PackedFlags private const int IsUnmanagedCallersOnlyAttributePopulatedBit = 0x1 << 26; private const int HasSetsRequiredMembersBit = 0x1 << 27; private const int HasSetsRequiredMembersPopulatedBit = 0x1 << 28; + private const int IsUnscopedRefBit = 0x1 << 29; + private const int IsUnscopedRefPopulatedBit = 0x1 << 30; private int _bits; @@ -140,6 +144,8 @@ public MethodKind MethodKind public bool IsUnmanagedCallersOnlyAttributePopulated => (_bits & IsUnmanagedCallersOnlyAttributePopulatedBit) != 0; public bool HasSetsRequiredMembers => (_bits & HasSetsRequiredMembersBit) != 0; public bool HasSetsRequiredMembersPopulated => (_bits & HasSetsRequiredMembersPopulatedBit) != 0; + public bool IsUnscopedRef => (_bits & IsUnscopedRefBit) != 0; + public bool IsUnscopedRefPopulated => (_bits & IsUnscopedRefPopulatedBit) != 0; #if DEBUG static PackedFlags() @@ -254,6 +260,14 @@ public bool InitializeSetsRequiredMembersBit(bool value) return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet); } + + public bool InitializeIsUnscopedRef(bool value) + { + int bitsToSet = IsUnscopedRefPopulatedBit; + if (value) bitsToSet |= IsUnscopedRefBit; + + return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet); + } } /// @@ -1610,5 +1624,20 @@ public override bool AreLocalsZeroed internal bool TestIsExtensionBitTrue => _packedFlags.IsExtensionMethod; internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable; + + internal sealed override bool HasUnscopedRefAttribute + { + get + { + if (!_packedFlags.IsUnscopedRefPopulated) + { + var moduleSymbol = _containingType.ContainingPEModule; + bool unscopedRef = moduleSymbol.Module.HasUnscopedRefAttribute(_handle); + _packedFlags.InitializeIsUnscopedRef(unscopedRef); + } + + return _packedFlags.IsUnscopedRef; + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index 8ba3eff6c790e..ccda345181abd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -293,7 +293,11 @@ private PEParameterSymbol( typeWithAnnotations = NullableTypeDecoder.TransformType(typeWithAnnotations, handle, moduleSymbol, accessSymbol: accessSymbol, nullableContext: nullableContext); typeWithAnnotations = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeWithAnnotations, handle, moduleSymbol); - if (refKind == RefKind.Out) + if (_moduleSymbol.Module.HasUnscopedRefAttribute(_handle)) + { + scope = DeclarationScope.Unscoped; + } + else if (refKind == RefKind.Out) { scope = DeclarationScope.RefScoped; } @@ -985,7 +989,9 @@ public override ImmutableArray DeclaringSyntaxReferences } } - internal sealed override DeclarationScope Scope => _packedFlags.Scope; + internal sealed override DeclarationScope DeclaredScope => _packedFlags.Scope; + + internal sealed override DeclarationScope EffectiveScope => DeclaredScope; public override ImmutableArray GetAttributes() { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs index f2f94b1228dd3..14224e42c3994 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs @@ -53,17 +53,20 @@ internal class PEPropertySymbol private struct PackedFlags { // Layout: - // |...........................|rr|c|n|s| + // |.........................|uu|rr|c|n|s| // // s = special name flag. 1 bit // n = runtime special name flag. 1 bit // c = call methods directly flag. 1 bit // r = Required member. 2 bits (1 bit for value + 1 completion bit). + // u = Unscoped ref. 2 bits (1 bit for value + 1 completion bit). private const int IsSpecialNameFlag = 1 << 0; private const int IsRuntimeSpecialNameFlag = 1 << 1; private const int CallMethodsDirectlyFlag = 1 << 2; private const int HasRequiredMemberAttribute = 1 << 4; private const int RequiredMemberCompletionBit = 1 << 5; + private const int HasUnscopedRefAttribute = 1 << 6; + private const int UnscopedRefCompletionBit = 1 << 7; private int _bits; @@ -92,6 +95,24 @@ public bool TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute return false; } + public void SetHasUnscopedRefAttribute(bool unscopedRef) + { + var bitsToSet = (unscopedRef ? HasUnscopedRefAttribute : 0) | UnscopedRefCompletionBit; + ThreadSafeFlagOperations.Set(ref _bits, bitsToSet); + } + + public bool TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute) + { + if ((_bits & UnscopedRefCompletionBit) != 0) + { + hasUnscopedRefAttribute = (_bits & HasUnscopedRefAttribute) != 0; + return true; + } + + hasUnscopedRefAttribute = false; + return false; + } + public bool IsSpecialName => (_bits & IsSpecialNameFlag) != 0; public bool IsRuntimeSpecialName => (_bits & IsRuntimeSpecialNameFlag) != 0; public bool CallMethodsDirectly => (_bits & CallMethodsDirectlyFlag) != 0; @@ -507,6 +528,21 @@ internal override bool IsRequired } } + internal sealed override bool HasUnscopedRefAttribute + { + get + { + if (!_flags.TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute)) + { + var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule; + hasUnscopedRefAttribute = containingPEModuleSymbol.Module.HasUnscopedRefAttribute(_handle); + _flags.SetHasUnscopedRefAttribute(hasUnscopedRefAttribute); + } + + return hasUnscopedRefAttribute; + } + } + public override ImmutableArray Parameters { get { return _parameters; } diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index b710bf43f30ce..340b48866c832 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -586,6 +586,8 @@ public bool IsConditional protected abstract bool HasSetsRequiredMembersImpl { get; } + internal abstract bool HasUnscopedRefAttribute { get; } + /// /// Some method kinds do not participate in overriding/hiding (e.g. constructors). /// diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs index a194aea394fcc..413a9e308c56c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs @@ -217,5 +217,13 @@ internal static CSharpSyntaxNode ExtractReturnTypeSyntax(this MethodSymbol metho return (CSharpSyntaxNode)CSharpSyntaxTree.Dummy.GetRoot(); } + + internal static bool IsValidUnscopedRefAttributeTarget(this MethodSymbol method) + { + return !method.IsStatic && + method.ContainingType?.IsStructType() == true && + !method.IsConstructor() && + !method.IsInitOnly; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs index ccc647939ee3a..4bb025069b9e3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs @@ -415,7 +415,17 @@ internal sealed override ObsoleteAttributeData? ObsoleteAttributeData /// internal abstract bool HasInterpolatedStringHandlerArgumentError { get; } - internal abstract DeclarationScope Scope { get; } + /// + /// The declared scope. From source, this is from the scope keyword + /// and any implicit scope, ignoring any UnscopedRefAttribute. + /// + internal abstract DeclarationScope DeclaredScope { get; } + + /// + /// The effective scope. This is from the declared scope and any + /// UnscopedRefAttribute. + /// + internal abstract DeclarationScope EffectiveScope { get; } protected sealed override bool IsHighestPriorityUseSiteErrorCode(int code) => code is (int)ErrorCode.ERR_UnsupportedCompilerFeature or (int)ErrorCode.ERR_BogusType; diff --git a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs index 245c42e1b0d21..025f377e0225e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs @@ -218,6 +218,8 @@ public abstract MethodSymbol SetMethod internal abstract bool MustCallMethodsDirectly { get; } + internal abstract bool HasUnscopedRefAttribute { get; } + /// /// Returns the overridden property, or null. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 31b4141c1479a..9bf922555216e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -590,6 +590,8 @@ public override int GetHashCode() protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + internal sealed override bool HasUnscopedRefAttribute => false; + #nullable enable private sealed class ReducedExtensionMethodParameterSymbol : WrappedParameterSymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs index cde90e9074875..39f7ebdd5567c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs @@ -255,6 +255,20 @@ public override ImmutableArray GetReturnTypeAttributes() return _lazyUnmanagedAttributeData; } + + internal override bool TryGetThisParameter(out ParameterSymbol? thisParameter) + { + if (!_underlyingMethod.TryGetThisParameter(out var underlyingParameter)) + { + thisParameter = null; + return false; + } + + thisParameter = underlyingParameter is { } + ? new ThisParameterSymbol(this) + : null; + return true; + } #nullable disable public override AssemblySymbol ContainingAssembly diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index bb92c1a6e5afb..a13a66a23baff 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -174,6 +174,8 @@ internal override bool IsMetadataFinal protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + internal sealed override bool HasUnscopedRefAttribute => false; + #endregion } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs index 35f0b30166b6a..a0c8fc9055f66 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs @@ -48,7 +48,8 @@ public SignatureOnlyParameterSymbol( public override bool IsDiscard { get { return false; } } - internal override DeclarationScope Scope => DeclarationScope.Unscoped; + internal override DeclarationScope DeclaredScope => DeclarationScope.Unscoped; + internal override DeclarationScope EffectiveScope => DeclaredScope; #region Not used by MethodSignatureComparer diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs index 0c2297b9ce522..58fc55748f0c9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs @@ -63,6 +63,8 @@ public SignatureOnlyPropertySymbol( public override string Name { get { return _name; } } + internal sealed override bool HasUnscopedRefAttribute => false; + #region Not used by PropertySignatureComparer internal override bool HasSpecialName { get { throw ExceptionUtilities.Unreachable; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index b00972c7de297..32cf7efe09d89 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -310,7 +310,7 @@ internal static bool RequiresScopedRefAttribute(ParameterSymbol parameter) { Debug.Assert(!parameter.IsThis); - var scope = parameter.Scope; + var scope = parameter.DeclaredScope; if (scope == DeclarationScope.Unscoped) { return false; @@ -648,7 +648,7 @@ private static void ReportParameterErrors( diagnostics.Add(ErrorCode.ERR_MethodArgCantBeRefAny, parameterSyntax.Location, parameter.Type); } - if (parameter.Scope == DeclarationScope.ValueScoped && !parameter.Type.IsErrorTypeOrRefLikeType()) + if (parameter.DeclaredScope == DeclarationScope.ValueScoped && !parameter.Type.IsErrorTypeOrRefLikeType()) { diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, parameterSyntax.Location); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs index 39d5cb9912a68..7532cd0c65ab9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs @@ -58,7 +58,8 @@ internal override bool IsMetadataOptional } } - internal sealed override DeclarationScope Scope => _originalParam.Scope; + internal sealed override DeclarationScope DeclaredScope => _originalParam.DeclaredScope; + internal sealed override DeclarationScope EffectiveScope => _originalParam.EffectiveScope; internal override ConstantValue ExplicitDefaultConstantValue { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 3c121938a6918..ca6bf2072e013 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -198,6 +198,22 @@ internal bool HasEnumeratorCancellationAttribute #nullable enable + internal sealed override DeclarationScope EffectiveScope + { + get + { + var scope = DeclaredScope; + if (scope != DeclarationScope.Unscoped && + HasUnscopedRefAttribute) + { + return DeclarationScope.Unscoped; + } + return scope; + } + } + + private bool HasUnscopedRefAttribute => GetEarlyDecodedWellKnownAttributeData()?.HasUnscopedRefAttribute == true; + internal static SyntaxNode? GetDefaultValueSyntaxForIsNullableAnalysisEnabled(ParameterSyntax? parameterSyntax) => parameterSyntax?.Default?.Value; @@ -593,6 +609,13 @@ internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAt { return EarlyDecodeAttributeForDefaultParameterValue(AttributeDescription.DateTimeConstantAttribute, ref arguments); } + else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.UnscopedRefAttribute)) + { + // We can't bind the attribute here because that might lead to a cycle. + // Instead, simply record that the attribute exists and bind later. + arguments.GetOrCreateData().HasUnscopedRefAttribute = true; + return (null, null); + } else if (!IsOnPartialImplementation(arguments.AttributeSyntax)) { if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.CallerLineNumberAttribute)) @@ -792,6 +815,27 @@ protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttribut { DecodeInterpolatedStringHandlerArgumentAttribute(ref arguments, diagnostics, index); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.UnscopedRefAttribute)) + { + if (!this.IsValidUnscopedRefAttributeTarget()) + { + diagnostics.Add(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, arguments.AttributeSyntaxOpt.Location); + } + } + } + + private bool IsValidUnscopedRefAttributeTarget() + { + switch (RefKind) + { + case RefKind.Out: + return true; + case RefKind.Ref: + var type = Type; + return type is null || type.IsErrorTypeOrRefLikeType(); + default: + return false; + } } private static bool? DecodeMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(AttributeDescription description, CSharpAttributeData attribute) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs index 1319cc53f55b5..355aa4969c23c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs @@ -1392,7 +1392,7 @@ internal static bool CheckValidScopedOverride( { var baseParameter = baseParameters[i]; var overrideParameter = overrideParameters[i + overrideParameterOffset]; - if (baseParameter.Scope != overrideParameter.Scope) + if (baseParameter.EffectiveScope != overrideParameter.EffectiveScope) { reportMismatchInParameterType(diagnostics, baseMethod, overrideMethod, overrideParameter, topLevel: true, extraArgument); hasErrors = true; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 020f765ada186..b22950a592363 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -570,6 +570,17 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut { DecodeUnmanagedCallersOnlyAttribute(ref arguments); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.UnscopedRefAttribute)) + { + if (this.IsValidUnscopedRefAttributeTarget()) + { + arguments.GetOrCreateData().HasUnscopedRefAttribute = true; + } + else + { + diagnostics.Add(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, arguments.AttributeSyntaxOpt.Location); + } + } else { var compilation = this.DeclaringCompilation; @@ -600,6 +611,8 @@ public override FlowAnalysisAnnotations FlowAnalysisAnnotations private static FlowAnalysisAnnotations DecodeFlowAnalysisAttributes(MethodWellKnownAttributeData attributeData) => attributeData?.HasDoesNotReturnAttribute == true ? FlowAnalysisAnnotations.DoesNotReturn : FlowAnalysisAnnotations.None; + internal sealed override bool HasUnscopedRefAttribute => GetDecodedWellKnownAttributeData()?.HasUnscopedRefAttribute == true; + private bool VerifyObsoleteAttributeAppliedToMethod( ref DecodeWellKnownAttributeArguments arguments, AttributeDescription description) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index 18e250286f61b..733cf610feedc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -143,7 +143,7 @@ internal SourceParameterSymbol WithCustomModifiersAndParamsCore(TypeSymbol newTy this.SyntaxReference, newIsParams, this.IsExtensionMethodThis, - this.Scope); + this.DeclaredScope); } // Local functions should never have custom modifiers @@ -160,7 +160,7 @@ internal SourceParameterSymbol WithCustomModifiersAndParamsCore(TypeSymbol newTy this.SyntaxReference, newIsParams, this.IsExtensionMethodThis, - this.Scope); + this.DeclaredScope); } internal sealed override bool RequiresCompletion @@ -222,7 +222,9 @@ public sealed override RefKind RefKind } } - internal sealed override DeclarationScope Scope => _scope; + internal sealed override DeclarationScope DeclaredScope => _scope; + + internal abstract override DeclarationScope EffectiveScope { get; } public sealed override string Name { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs index 9ada805de7ade..c4c2919c61953 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbolBase.cs @@ -101,7 +101,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r if (ParameterHelpers.RequiresScopedRefAttribute(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeScopedRefAttribute(this, Scope)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeScopedRefAttribute(this, DeclaredScope)); } if (type.Type.ContainsTupleNames()) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index 9b9a73fc39f4d..4a6db766bde97 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -1313,7 +1313,31 @@ protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttribut MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(diagnostics, arguments.AttributeSyntaxOpt); CSharpAttributeData.DecodeMemberNotNullWhenAttribute(ContainingType, ref arguments); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.UnscopedRefAttribute)) + { + if (this.IsValidUnscopedRefAttributeTarget()) + { + arguments.GetOrCreateData().HasUnscopedRefAttribute = true; + } + else + { + diagnostics.Add(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, arguments.AttributeSyntaxOpt.Location); + } + } + } + +#nullable enable + private bool IsValidUnscopedRefAttributeTarget() + { + return isNullOrValidAccessor(_getMethod) && + isNullOrValidAccessor(_setMethod); + + static bool isNullOrValidAccessor(MethodSymbol? accessor) + { + return accessor is null || accessor.IsValidUnscopedRefAttributeTarget(); + } } +#nullable disable internal bool HasDisallowNull { @@ -1369,6 +1393,8 @@ internal ImmutableArray MemberNotNullAttributeIfExists internal ImmutableArray MemberNotNullWhenAttributeIfExists => FindAttributes(AttributeDescription.MemberNotNullWhenAttribute); + internal sealed override bool HasUnscopedRefAttribute => GetDecodedWellKnownAttributeData()?.HasUnscopedRefAttribute == true; + private SourceAttributeData FindAttribute(AttributeDescription attributeDescription) => (SourceAttributeData)GetAttributes().First(a => a.IsTargetAttribute(this, attributeDescription)); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs index aad570a7aaa04..b11b6b6489261 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs @@ -128,5 +128,7 @@ internal override ConstantValue DefaultValueFromAttributes { get { return ConstantValue.NotAvailable; } } + + internal override DeclarationScope EffectiveScope => DeclaredScope; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs index f2daba205129c..bd7fc894e19ab 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs @@ -172,6 +172,37 @@ internal override MarshalPseudoCustomAttributeData? MarshallingInformation internal override bool HasInterpolatedStringHandlerArgumentError => false; - internal override DeclarationScope Scope => ContainingType?.TypeKind != TypeKind.Struct ? DeclarationScope.Unscoped : DeclarationScope.RefScoped; + internal override DeclarationScope DeclaredScope + => _containingType.IsStructType() ? DeclarationScope.RefScoped : DeclarationScope.Unscoped; + + internal override DeclarationScope EffectiveScope + { + get + { + var scope = DeclaredScope; + if (scope != DeclarationScope.Unscoped && + hasUnscopedRefAttribute(_containingMethod)) + { + return DeclarationScope.Unscoped; + } + return scope; + + static bool hasUnscopedRefAttribute(MethodSymbol? containingMethod) + { + if (containingMethod is { }) + { + if (containingMethod.HasUnscopedRefAttribute == true) + { + return true; + } + if (containingMethod.AssociatedSymbol is PropertySymbol { HasUnscopedRefAttribute: true }) + { + return true; + } + } + return false; + } + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 69d0a214d5b41..b836f55e63077 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -309,6 +309,8 @@ private static BoundCall CreateParameterlessCall(CSharpSyntaxNode syntax, BoundE protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + internal sealed override bool HasUnscopedRefAttribute => false; + /// A synthesized entrypoint that forwards all calls to an async Main Method internal sealed class AsyncForwardEntryPoint : SynthesizedEntryPointSymbol { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs index 893bb85ea8ed1..9c14068b8f66e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs @@ -337,5 +337,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l internal sealed override bool IsNullableAnalysisEnabled() => false; protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + + internal sealed override bool HasUnscopedRefAttribute => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs index 880613454fab0..fc53ab75d5bd6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs @@ -78,5 +78,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; internal override bool IsNullableAnalysisEnabled() => false; + + internal sealed override bool HasUnscopedRefAttribute => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index 6777f7dcc2a90..46466ac5fed98 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -425,6 +425,8 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + internal sealed override bool HasUnscopedRefAttribute => false; + public override bool Equals(Symbol obj, TypeCompareKind compareKind) { if (obj == (object)this) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index cbd6796524734..bd3bbe032e006 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -162,7 +162,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r if (ParameterHelpers.RequiresScopedRefAttribute(this)) { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeScopedRefAttribute(this, Scope)); + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeScopedRefAttribute(this, DeclaredScope)); } if (type.Type.ContainsTupleNames() && @@ -188,7 +188,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r internal override bool HasInterpolatedStringHandlerArgumentError => false; - internal sealed override DeclarationScope Scope => _scope; + internal sealed override DeclarationScope DeclaredScope => _scope; + + internal sealed override DeclarationScope EffectiveScope => DeclaredScope; } internal sealed class SynthesizedParameterSymbol : SynthesizedParameterSymbolBase @@ -251,7 +253,7 @@ internal static ImmutableArray DeriveParameters(MethodSymbol so oldParam.Ordinal, oldParam.RefKind, oldParam.Name, - oldParam.Scope, + oldParam.DeclaredScope, oldParam.RefCustomModifiers, baseParameterForAttributes: null)); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs index 948bd9f64071d..36d0885c90137 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs @@ -430,5 +430,7 @@ private bool CalculateShouldEmit(ImmutableArray boundInitializ } protected sealed override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable; + + internal sealed override bool HasUnscopedRefAttribute => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs index 06f156ca7690b..43936f534a57a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs @@ -360,5 +360,7 @@ internal override bool GenerateDebugInfo internal override bool IsInitOnly => UnderlyingMethod.IsInitOnly; protected sealed override bool HasSetsRequiredMembersImpl => UnderlyingMethod.HasSetsRequiredMembers; + + internal sealed override bool HasUnscopedRefAttribute => UnderlyingMethod.HasUnscopedRefAttribute; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs index 9bb91786d346c..d9aafec74e7f1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs @@ -152,7 +152,9 @@ public override string GetDocumentationCommentXml(CultureInfo? preferredCulture return _underlyingParameter.GetDocumentationCommentXml(preferredCulture, expandIncludes, cancellationToken); } - internal sealed override DeclarationScope Scope => _underlyingParameter.Scope; + internal sealed override DeclarationScope DeclaredScope => _underlyingParameter.DeclaredScope; + + internal sealed override DeclarationScope EffectiveScope => _underlyingParameter.EffectiveScope; #endregion } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs index e9f62600a2a8a..8ffb1ce4fdd59 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs @@ -163,6 +163,8 @@ public override bool IsExtern internal sealed override bool IsRequired => _underlyingProperty.IsRequired; + internal sealed override bool HasUnscopedRefAttribute => _underlyingProperty.HasUnscopedRefAttribute; + internal override ObsoleteAttributeData ObsoleteAttributeData { get diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index b009aa8742a86..12d27a02d0d82 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1572,6 +1572,11 @@ {0} má atribut UnmanagedCallersOnly a nedá se převést na typ delegáta. Pro tuto metodu získejte ukazatel na funkci. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. {0} vyžaduje funkci kompilátoru {1}, což tato verze kompilátoru C# nepodporuje. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 0b57395eb50c7..2aaf52461f390 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1572,6 +1572,11 @@ "{0}" ist mit dem Attribut "UnmanagedCallersOnly" versehen und kann nicht in einen Delegattyp konvertiert werden. Rufen Sie einen Funktionszeiger auf diese Methode ab. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}' erfordert die Compilerfunktion '{1}', die von dieser Version des C#-Compilers nicht unterstützt wird. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index fa09c09c3822a..3ff870d217ce2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1572,6 +1572,11 @@ ' {0} ' tiene un atributo ' UnmanagedCallersOnly ' y no se puede convertir en un tipo de delegado. Obtenga un puntero de función a este método. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}' requiere la característica del compilador '{1}', que no es compatible con esta versión del compilador de C#. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 1abd33a14f021..bc9b8cf0d6c55 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1572,6 +1572,11 @@ '{0}' est attribué avec 'UnmanagedCallersOnly' et ne peut pas être converti en type délégué. Obtenez un pointeur de fonction vers cette méthode. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}' nécessite la fonctionnalité de compilateur '{1}', qui n’est pas prise en charge par cette version du compilateur C#. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 367e9626e8393..6ad538021cba8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1572,6 +1572,11 @@ '{0}', a cui è assegnato l'attributo 'UnmanagedCallersOnly', non può essere convertito in un tipo delegato. Ottenere un puntatore a funzione per questo metodo. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}' richiede la funzionalità del compilatore '{1}', che non è supportata da questa versione del compilatore C#. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 1128c3ca900b7..c5c07e059409b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1572,6 +1572,11 @@ '{0}' は 'UnmanagedCallersOnly' 属性が設定されているため、デリゲート型に変換できません。このメソッドへの関数ポインターを取得してください。 UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}' にはコンパイラ機能 '{1}' が必要ですが、このバージョンのC## コンパイラではサポートされていません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 6792a71260d8c..746e1aa85b7c9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1572,6 +1572,11 @@ '{0}'에는 'UnmanagedCallersOnly' 특성이 지정되어 있으며 이 항목은 대리자 형식으로 변환할 수 없습니다. 이 메서드에 대한 함수 포인터를 가져오세요. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}'에는 이 버전의 C # 컴파일러에서 지원되지 않는 컴파일러 기능 '{1}'이(가) 필요합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index cbd9a7e9dda3c..dfac99057d2a9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1572,6 +1572,11 @@ Element „{0}” ma atrybut „UnmanagedCallersOnly” i nie można go przekonwertować na typ delegowany. Uzyskaj wskaźnik funkcji do tej metody. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. „{0}” wymaga funkcji kompilatora „{1}”, która nie jest obsługiwana przez tę wersję kompilatora języka C#. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index e97227e174e19..c273dca4f9b64 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1572,6 +1572,11 @@ '{0}' foi atribuído com 'UnmanagedCallersOnly' e não pode ser convertido em um tipo delegado. Obtenha um ponteiro de função para esse método. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}' requer o recurso de compilador '{1}', o que não é suportado por esta versão do compilador de C#. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index f64cfb57740bf..8c3444d5d0283 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1572,6 +1572,11 @@ "{0}" имеет атрибут "UnmanagedCallersOnly" и не может быть преобразован в тип делегата. Получите указатель на функцию для этого метода. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. Для "{0}" требуется функция компилятора "{1}", которая не поддерживается в этой версии компилятора C#. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 832a9b9a13c4e..962a354d8cbe5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1572,6 +1572,11 @@ '{0}', 'UnmanagedCallersOnly' özniteliğine sahip ve temsilci türüne dönüştürülemez. Bu yöntem için bir işlev işaretçisi edinin. UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}', C# derleyicisinin bu sürümü tarafından desteklenmeyen '{1}' derleyici özelliğini gerektirir. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 7b2aca9973b9c..0828401b045c7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1572,6 +1572,11 @@ “{0}”使用 "UnmanagedCallersOnly" 进行特性化,无法转换为委托类型。请获取指向此方法的函数指针。 UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}' 需要编译器功能 '{1}',此版本的 C# 编译器不支持此功能。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 2c32044926414..922e54b5c06fb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1572,6 +1572,11 @@ '{0}' 使用 'UnmanagedCallersOnly' 屬性化,因此無法轉換為委派類型。取得此方法的函式指標。 UnmanagedCallersOnly is not localizable. + + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. '{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler. diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs index f9f3b86e81005..e1b0cf7967028 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_CallerInfoAttributes.cs @@ -5818,5 +5818,27 @@ void M(int i, [CallerArgumentExpression(""i"")] in string s = ""default value"") CompileAndVerify(comp, expectedOutput: "1 + 1").VerifyDiagnostics(); } + + [Fact] + public void CallerArgumentExpression_Cycle() + { + string source = +@"namespace System.Runtime.CompilerServices +{ + public sealed class CallerArgumentExpressionAttribute : Attribute + { + public CallerArgumentExpressionAttribute([CallerArgumentExpression(nameof(parameterName))] string parameterName) + { + ParameterName = parameterName; + } + public string ParameterName { get; } + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (5,51): error CS8964: The CallerArgumentExpressionAttribute may only be applied to parameters with default values + // public CallerArgumentExpressionAttribute([CallerArgumentExpression(nameof(parameterName))] string parameterName) + Diagnostic(ErrorCode.ERR_BadCallerArgumentExpressionParamWithoutDefaultValue, "CallerArgumentExpression").WithLocation(5, 51)); + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs index 8363034cc9644..bca031598202b 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_LifetimeAnnotation.cs @@ -216,22 +216,22 @@ static void Main() var method = comp.GetMember("A.F1"); Assert.Equal("void A.F1(scoped R r)", method.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped))); var parameter = method.Parameters[0]; - Assert.Equal(DeclarationScope.ValueScoped, parameter.Scope); + Assert.Equal(DeclarationScope.ValueScoped, parameter.DeclaredScope); method = comp.GetMember("A.F2"); Assert.Equal("void A.F2(System.Int32 y)", method.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped))); parameter = method.Parameters[0]; - Assert.Equal(DeclarationScope.Unscoped, parameter.Scope); + Assert.Equal(DeclarationScope.Unscoped, parameter.DeclaredScope); method = comp.GetMember("A.F3"); Assert.Equal("void A.F3(System.Object x, scoped ref System.Int32 y)", method.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped))); parameter = method.Parameters[1]; - Assert.Equal(DeclarationScope.RefScoped, parameter.Scope); + Assert.Equal(DeclarationScope.RefScoped, parameter.DeclaredScope); method = comp.GetMember("A.F4"); Assert.Equal("void A.F4(scoped ref R r)", method.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped))); parameter = method.Parameters[0]; - Assert.Equal(DeclarationScope.RefScoped, parameter.Scope); + Assert.Equal(DeclarationScope.RefScoped, parameter.DeclaredScope); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 92854472e3261..c982a430e8886 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -7250,7 +7250,7 @@ static void Main() private static void VerifyParameterSymbol(ParameterSymbol parameter, string expectedDisplayString, RefKind expectedRefKind, DeclarationScope expectedScope) { Assert.Equal(expectedRefKind, parameter.RefKind); - Assert.Equal(expectedScope, parameter.Scope); + Assert.Equal(expectedScope, parameter.EffectiveScope); Assert.Equal(expectedDisplayString, parameter.ToDisplayString(displayFormatWithScoped)); var attribute = parameter.GetAttributes().FirstOrDefault(a => a.GetTargetAttributeSignatureIndex(parameter, AttributeDescription.ScopedRefAttribute) != -1); @@ -12592,5 +12592,1476 @@ public void ScopedReserved_Alias_Escaped() Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using @scoped = System.Int32;").WithLocation(1, 1) ); } + + [Theory] + [InlineData("struct")] + [InlineData("ref struct")] + public void UnscopedRefAttribute_Method_01(string type) + { + var source = +$@"using System.Diagnostics.CodeAnalysis; +{type} S1 +{{ + private int _field; + public ref int GetField1() => ref _field; // 1 + [UnscopedRef] public ref int GetField2() => ref _field; +}}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (5,39): error CS8170: Struct members cannot return 'this' or other instance members by reference + // public ref int GetField1() => ref _field; // 1 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "_field").WithLocation(5, 39)); + } + + [Theory] + [InlineData("struct")] + [InlineData("ref struct")] + public void UnscopedRefAttribute_Method_02(string type) + { + var source = +$@"using System.Diagnostics.CodeAnalysis; +{type} S +{{ + ref S F1() + {{ + ref S s1 = ref this; + return ref s1; + }} + [UnscopedRef] ref S F2() + {{ + ref S s2 = ref this; + return ref s2; + }} +}}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (7,20): error CS8157: Cannot return 's1' by reference because it was initialized to a value that cannot be returned by reference + // return ref s1; + Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "s1").WithArguments("s1").WithLocation(7, 20)); + } + + [CombinatorialData] + [Theory] + public void UnscopedRefAttribute_Method_03(bool useCompilationReference) + { + var sourceA = +@"using System.Diagnostics.CodeAnalysis; +public struct S +{ + private T _t; + public ref T F1() => throw null; + [UnscopedRef] public ref T F2() => ref _t; +}"; + var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition }); + comp.VerifyEmitDiagnostics(); + var refA = AsReference(comp, useCompilationReference); + + var sourceB = +@"class Program +{ + static ref int F1() + { + var s = new S(); + return ref s.F1(); + } + static ref int F2() + { + var s = new S(); + return ref s.F2(); // 1 + } +}"; + comp = CreateCompilation(sourceB, references: new[] { refA }); + // https://github.com/dotnet/roslyn/issues/62791: Missing errors. + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void UnscopedRefAttribute_Property_01() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +struct S1 +{ + private int _field; + public ref int Property1 => ref _field; // 1 + [UnscopedRef] public ref int Property2 => ref _field; +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (5,37): error CS8170: Struct members cannot return 'this' or other instance members by reference + // public ref int Property1 => ref _field; // 1 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "_field").WithLocation(5, 37)); + } + + [CombinatorialData] + [Theory] + public void UnscopedRefAttribute_Property_02(bool useCompilationReference) + { + var sourceA = +@"using System.Diagnostics.CodeAnalysis; +public struct S +{ + private T _t; + public ref T P1 => throw null; + [UnscopedRef] public ref T P2 => ref _t; +}"; + var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition }); + comp.VerifyEmitDiagnostics(); + var refA = AsReference(comp, useCompilationReference); + + var sourceB = +@"class Program +{ + static ref int F1() + { + var s = new S(); + return ref s.P1; + } + static ref int F2() + { + var s = new S(); + return ref s.P2; // 1 + } +}"; + comp = CreateCompilation(sourceB, references: new[] { refA }); + // https://github.com/dotnet/roslyn/issues/62791: Missing errors. + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void UnscopedRefAttribute_Property_03() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +struct S +{ + [UnscopedRef] object P1 { get; } + [UnscopedRef] object P2 { get; set; } + [UnscopedRef] object P3 { get; init; } // 1 + object P5 + { + [UnscopedRef] get; + [UnscopedRef] set; + } + object P6 + { + [UnscopedRef] get; + [UnscopedRef] init; // 2 + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition, IsExternalInitTypeDefinition }); + comp.VerifyDiagnostics( + // (6,6): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] object P3 { get; init; } // 1 + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(6, 6), + // (15,10): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] init; // 2 + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(15, 10)); + } + + [Theory] + [InlineData("struct")] + [InlineData("ref struct")] + public void UnscopedRefAttribute_Accessor_01(string type) + { + var source = +$@"using System.Diagnostics.CodeAnalysis; +{type} S +{{ + private T _t; + ref T P1 + {{ + get => ref _t; // 1 + }} + ref T P2 + {{ + [UnscopedRef] + get => ref _t; + }} +}}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (7,20): error CS8170: Struct members cannot return 'this' or other instance members by reference + // get => ref _t; // 1 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "_t").WithLocation(7, 20)); + } + + [Fact] + public void UnscopedRefAttribute_Accessor_02() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +ref struct R +{ + public ref T F; +} +struct A +{ + R this[int i] + { + get { return default; } + set { value.F = ref this; } // 1 + } +} +struct B +{ + R this[int i] + { + get { return default; } + [UnscopedRef] + set { value.F = ref this; } + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (11,15): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. + // set { value.F = ref this; } // 1 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "value.F = ref this").WithArguments("F", "this").WithLocation(11, 15)); + } + + [Fact] + public void UnscopedRefAttribute_Accessor_03() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +ref struct R +{ + public ref T F; +} +struct A +{ + R this[int i] + { + get { return default; } + init { value.F = ref this; } // 1 + } +} +struct B +{ + R this[int i] + { + get { return default; } + [UnscopedRef] + init { value.F = ref this; } // 2 + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition, IsExternalInitTypeDefinition }); + comp.VerifyDiagnostics( + // (11,16): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. + // init { value.F = ref this; } // 1 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "value.F = ref this").WithArguments("F", "this").WithLocation(11, 16), + // (19,10): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(19, 10), + // (20,16): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. + // init { value.F = ref this; } // 2 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "value.F = ref this").WithArguments("F", "this").WithLocation(20, 16)); + } + + [Theory] + [CombinatorialData] + public void UnscopedRefAttribute_SubstitutedMembers(bool useCompilationReference) + { + var sourceA = +@"using System.Diagnostics.CodeAnalysis; +public ref struct R1 +{ + private T _t; + public R1(T t) { _t = t; } + public ref T Get() => ref this[0]; + public ref T this[int index] => throw null; +} +public ref struct R2 +{ + private T _t; + public R2(T t) { _t = t; } + [UnscopedRef] public ref T Get() => ref this[0]; + [UnscopedRef] public ref T this[int index] => ref _t; +}"; + var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition }); + comp.VerifyEmitDiagnostics(); + + var refA = AsReference(comp, useCompilationReference); + + var sourceB = +@"class Program +{ + static ref int F1(bool b) + { + R1 r1 = new R1(1); + if (b) return ref r1.Get(); + return ref r1[0]; + } + static ref int F2(bool b) + { + R2 r2 = new R2(2); + if (b) return ref r2.Get(); // 1 + return ref r2[0]; // 2 + } +}"; + comp = CreateCompilation(sourceB, references: new[] { refA }); + // https://github.com/dotnet/roslyn/issues/62791: Missing errors. + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().Where(v => v.Identifier.Text is "r1" or "r2").ToArray(); + var types = decls.Select(d => (NamedTypeSymbol)model.GetDeclaredSymbol(d).GetSymbol().Type).ToArray(); + + var type = types[0]; + Assert.Equal("R1", type.ToTestDisplayString()); + VerifyParameterSymbol(type.GetMethod("Get").ThisParameter, "scoped ref R1 this", RefKind.Ref, DeclarationScope.RefScoped); + VerifyParameterSymbol(type.GetMethod("get_Item").ThisParameter, "scoped ref R1 this", RefKind.Ref, DeclarationScope.RefScoped); + + type = types[1]; + Assert.Equal("R2", type.ToTestDisplayString()); + VerifyParameterSymbol(type.GetMethod("Get").ThisParameter, "ref R2 this", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(type.GetMethod("get_Item").ThisParameter, "ref R2 this", RefKind.Ref, DeclarationScope.Unscoped); + } + + [Fact] + public void UnscopedRefAttribute_RetargetingMembers() + { + var sourceA = +@"using System.Diagnostics.CodeAnalysis; +public ref struct R1 +{ + private readonly T _t; + public R1(T t) { _t = t; } + public ref readonly T Get() => ref this[0]; + public ref readonly T this[int index] => ref _t; // 1 +} +public ref struct R2 +{ + private readonly T _t; + public R2(T t) { _t = t; } + [UnscopedRef] public ref readonly T Get() => ref this[0]; + [UnscopedRef] public ref readonly T this[int index] => ref _t; +}"; + var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Mscorlib40); + comp.VerifyDiagnostics( + // (7,50): error CS8170: Struct members cannot return 'this' or other instance members by reference + // public ref readonly T this[int index] => ref _t; // 1 + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "_t").WithLocation(7, 50)); + + var refA = comp.ToMetadataReference(); + + var sourceB = +@"class Program +{ + static ref readonly int F1(bool b) + { + R1 r1 = new R1(1); + if (b) return ref r1.Get(); + return ref r1[0]; + } + static ref readonly int F2(bool b) + { + R2 r2 = new R2(2); + if (b) return ref r2.Get(); // 1 + return ref r2[0]; // 2 + } +}"; + comp = CreateCompilation(sourceB, new[] { refA }, targetFramework: TargetFramework.Mscorlib45); + // https://github.com/dotnet/roslyn/issues/62791: Missing errors. + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var decls = tree.GetRoot().DescendantNodes().OfType().Where(v => v.Identifier.Text is "r1" or "r2").ToArray(); + var types = decls.Select(d => (NamedTypeSymbol)model.GetDeclaredSymbol(d).GetSymbol().Type).ToArray(); + + var type = types[0]; + var underlyingType = (RetargetingNamedTypeSymbol)type.OriginalDefinition; + Assert.Equal("R1", type.ToTestDisplayString()); + VerifyParameterSymbol(type.GetMethod("Get").ThisParameter, "scoped ref R1 this", RefKind.Ref, DeclarationScope.RefScoped); + VerifyParameterSymbol(type.GetMethod("get_Item").ThisParameter, "scoped ref R1 this", RefKind.Ref, DeclarationScope.RefScoped); + + type = types[1]; + underlyingType = (RetargetingNamedTypeSymbol)type.OriginalDefinition; + Assert.Equal("R2", type.ToTestDisplayString()); + VerifyParameterSymbol(type.GetMethod("Get").ThisParameter, "ref R2 this", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(type.GetMethod("get_Item").ThisParameter, "ref R2 this", RefKind.Ref, DeclarationScope.Unscoped); + } + + [Fact] + public void UnscopedRefAttribute_Constructor() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +ref struct R +{ + public ref T F; +} +struct A +{ + A(R r) + { + r.F = ref this; // 1 + } +} +struct B +{ + [UnscopedRef] + B(R r) + { + r.F = ref this; // 2 + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (10,9): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. + // r.F = ref this; // 1 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r.F = ref this").WithArguments("F", "this").WithLocation(10, 9), + // (15,6): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(15, 6), + // (18,9): error CS8374: Cannot ref-assign 'this' to 'F' because 'this' has a narrower escape scope than 'F'. + // r.F = ref this; // 2 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "r.F = ref this").WithArguments("F", "this").WithLocation(18, 9)); + } + + [Fact] + public void UnscopedRefAttribute_StaticMembers() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +struct S +{ + [UnscopedRef] static S() { } // 1 + [UnscopedRef] static object F() => null; // 2 + [UnscopedRef] static object P => null; // 3 +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (4,6): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] static S() { } // 1 + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(4, 6), + // (5,6): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] static object F() => null; // 2 + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(5, 6), + // (6,6): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] static object P => null; // 3 + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(6, 6)); + } + + [Fact] + public void UnscopedRefAttribute_OtherTypes() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +class C +{ + [UnscopedRef] object F1() => null; // 1 +} +record R +{ + [UnscopedRef] object F2() => null; // 2 +} +record struct S +{ + [UnscopedRef] object F3() => null; +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (4,6): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] object F1() => null; // 1 + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(4, 6), + // (8,6): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // [UnscopedRef] object F2() => null; // 2 + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(8, 6)); + } + + [WorkItem(62691, "https://github.com/dotnet/roslyn/issues/62691")] + [Fact] + public void UnscopedRefAttribute_RefRefStructParameter_01() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +ref struct R { } +class Program +{ + static ref R ReturnRefStructRef(bool b, ref R x, [UnscopedRef] ref R y) + { + if (b) + return ref x; // 1 + else + return ref y; + } +}"; + // https://github.com/dotnet/roslyn/issues/62691: An error should be reported for 'return ref x;'. + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics(); + + var parameters = comp.GetMember("Program.ReturnRefStructRef").Parameters; + VerifyParameterSymbol(parameters[1], "ref R x", RefKind.Ref, DeclarationScope.Unscoped); // https://github.com/dotnet/roslyn/issues/62691: Should be DeclarationScope.RefScoped. + VerifyParameterSymbol(parameters[2], "ref R y", RefKind.Ref, DeclarationScope.Unscoped); + } + + [CombinatorialData] + [Theory] + public void UnscopedRefAttribute_RefRefStructParameter_02(bool useCompilationReference) + { + var sourceA = +@"using System.Diagnostics.CodeAnalysis; +public ref struct R +{ + public ref T F; + public R(ref T t) { F = ref t; } +} +public class A +{ + public ref T F1A(ref R r1) + { + return ref r1.F; + } + public ref T F2A(scoped ref R r2) + { + return ref r2.F; + } + public ref T F3A([UnscopedRef] ref R r3) + { + return ref r3.F; + } + public ref T F4A([UnscopedRef] scoped ref R r4) + { + return ref r4.F; + } +}"; + var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition }); + comp.VerifyEmitDiagnostics(); + var refA = AsReference(comp, useCompilationReference); + + var sourceB1 = +@"class B1 : A +{ + ref int F1B() + { + int i = 1; + var r = new R(ref i); + return ref F1A(ref r); // 1 + } + ref int F2B() + { + int i = 2; + var r = new R(ref i); + return ref F2A(ref r); // 2 + } + ref int F3B() + { + int i = 3; + var r = new R(ref i); + return ref F3A(ref r); // 3 + } + ref int F4B() + { + int i = 4; + var r = new R(ref i); + return ref F4A(ref r); // 4 + } +}"; + comp = CreateCompilation(sourceB1, references: new[] { refA }); + // https://github.com/dotnet/roslyn/issues/62791: Missing error // 2. + comp.VerifyEmitDiagnostics( + // (7,20): error CS8347: Cannot use a result of 'A.F1A(ref R)' in this context because it may expose variables referenced by parameter 'r1' outside of their declaration scope + // return ref F1A(ref r); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "F1A(ref r)").WithArguments("A.F1A(ref R)", "r1").WithLocation(7, 20), + // (7,28): error CS8168: Cannot return local 'r' by reference because it is not a ref local + // return ref F1A(ref r); // 1 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "r").WithArguments("r").WithLocation(7, 28), + // (19,20): error CS8347: Cannot use a result of 'A.F3A(ref R)' in this context because it may expose variables referenced by parameter 'r3' outside of their declaration scope + // return ref F3A(ref r); // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "F3A(ref r)").WithArguments("A.F3A(ref R)", "r3").WithLocation(19, 20), + // (19,28): error CS8168: Cannot return local 'r' by reference because it is not a ref local + // return ref F3A(ref r); // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "r").WithArguments("r").WithLocation(19, 28), + // (25,20): error CS8347: Cannot use a result of 'A.F4A(ref R)' in this context because it may expose variables referenced by parameter 'r4' outside of their declaration scope + // return ref F4A(ref r); // 4 + Diagnostic(ErrorCode.ERR_EscapeCall, "F4A(ref r)").WithArguments("A.F4A(ref R)", "r4").WithLocation(25, 20), + // (25,28): error CS8168: Cannot return local 'r' by reference because it is not a ref local + // return ref F4A(ref r); // 4 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "r").WithArguments("r").WithLocation(25, 28)); + + var baseType = comp.GetMember("B1").BaseTypeNoUseSiteDiagnostics; + VerifyParameterSymbol(baseType.GetMethod("F1A").Parameters[0], "ref R r1", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F2A").Parameters[0], "scoped ref R r2", RefKind.Ref, DeclarationScope.RefScoped); + VerifyParameterSymbol(baseType.GetMethod("F3A").Parameters[0], "ref R r3", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F4A").Parameters[0], "ref R r4", RefKind.Ref, DeclarationScope.Unscoped); + + var sourceB2 = +@"class B2 : A +{ + ref int F1B(ref int i) + { + var r = new R(ref i); + return ref F1A(ref r); // 1 + } + ref int F2B(ref int i) + { + var r = new R(ref i); + return ref F2A(ref r); + } + ref int F3B(ref int i) + { + var r = new R(ref i); + return ref F3A(ref r); // 2 + } + ref int F4B(ref int i) + { + var r = new R(ref i); + return ref F4A(ref r); // 3 + } +}"; + comp = CreateCompilation(sourceB2, references: new[] { refA }); + comp.VerifyEmitDiagnostics( + // (6,20): error CS8347: Cannot use a result of 'A.F1A(ref R)' in this context because it may expose variables referenced by parameter 'r1' outside of their declaration scope + // return ref F1A(ref r); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "F1A(ref r)").WithArguments("A.F1A(ref R)", "r1").WithLocation(6, 20), + // (6,28): error CS8168: Cannot return local 'r' by reference because it is not a ref local + // return ref F1A(ref r); // 1 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "r").WithArguments("r").WithLocation(6, 28), + // (16,20): error CS8347: Cannot use a result of 'A.F3A(ref R)' in this context because it may expose variables referenced by parameter 'r3' outside of their declaration scope + // return ref F3A(ref r); // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "F3A(ref r)").WithArguments("A.F3A(ref R)", "r3").WithLocation(16, 20), + // (16,28): error CS8168: Cannot return local 'r' by reference because it is not a ref local + // return ref F3A(ref r); // 2 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "r").WithArguments("r").WithLocation(16, 28), + // (21,20): error CS8347: Cannot use a result of 'A.F4A(ref R)' in this context because it may expose variables referenced by parameter 'r4' outside of their declaration scope + // return ref F4A(ref r); // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "F4A(ref r)").WithArguments("A.F4A(ref R)", "r4").WithLocation(21, 20), + // (21,28): error CS8168: Cannot return local 'r' by reference because it is not a ref local + // return ref F4A(ref r); // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "r").WithArguments("r").WithLocation(21, 28)); + } + + [Fact] + public void UnscopedRefAttribute_OutParameter_01() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +class Program +{ + static ref int ReturnOut(bool b, out int x, [UnscopedRef] out int y) + { + x = 1; + y = 2; + if (b) + return ref x; // 1 + else + return ref y; + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (9,24): error CS8166: Cannot return a parameter by reference 'x' because it is not a ref parameter + // return ref x; // 1 + Diagnostic(ErrorCode.ERR_RefReturnParameter, "x").WithArguments("x").WithLocation(9, 24)); + + var parameters = comp.GetMember("Program.ReturnOut").Parameters; + VerifyParameterSymbol(parameters[1], "out System.Int32 x", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(parameters[2], "out System.Int32 y", RefKind.Out, DeclarationScope.Unscoped); + } + + [CombinatorialData] + [Theory] + public void UnscopedRefAttribute_OutParameter_02(bool useCompilationReference) + { + var sourceA = +@"using System.Diagnostics.CodeAnalysis; +public class A +{ + public ref T F1A(out T t1) + { + throw null; + } + public ref T F2A(scoped out T t2) + { + throw null; + } + public ref T F3A([UnscopedRef] out T t3) + { + t3 = default; + return ref t3; + } + public ref T F4A([UnscopedRef] scoped out T t4) + { + t4 = default; + return ref t4; + } +}"; + var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition }); + comp.VerifyEmitDiagnostics(); + var refA = AsReference(comp, useCompilationReference); + + var sourceB = +@"class B : A +{ + ref int F1B() + { + int i = 1; + return ref F1A(out i); + } + ref int F2B() + { + int i = 2; + return ref F2A(out i); + } + ref int F3B() + { + int i = 3; + return ref F3A(out i); // 1 + } + ref int F4B() + { + int i = 4; + return ref F4A(out i); // 2 + } +}"; + comp = CreateCompilation(sourceB, references: new[] { refA }); + // https://github.com/dotnet/roslyn/issues/62791: Missing errors. + comp.VerifyEmitDiagnostics(); + + var baseType = comp.GetMember("B").BaseTypeNoUseSiteDiagnostics; + VerifyParameterSymbol(baseType.GetMethod("F1A").Parameters[0], "out System.Int32 t1", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(baseType.GetMethod("F2A").Parameters[0], "out System.Int32 t2", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(baseType.GetMethod("F3A").Parameters[0], "out System.Int32 t3", RefKind.Out, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F4A").Parameters[0], "out System.Int32 t4", RefKind.Out, DeclarationScope.Unscoped); + } + + [Fact] + public void UnscopedRefAttribute_RefParameter() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +public class A +{ + public ref T F1A(ref T t1) => ref t1; + public ref T F2A(scoped ref T t2) => throw null; + public ref T F3A([UnscopedRef] ref T t3) => ref t3; + public ref T F4A([UnscopedRef] scoped ref T t4) => ref t4; +} +class B : A +{ + ref int F1B() + { + int i = 1; + return ref F1A(ref i); // 1 + } + ref int F2B() + { + int i = 2; + return ref F2A(ref i); + } + ref int F3B() + { + int i = 3; + return ref F3A(ref i); // 2 + } + ref int F4B() + { + int i = 4; + return ref F4A(ref i); // 3 + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyEmitDiagnostics( + // (6,23): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // public ref T F3A([UnscopedRef] ref T t3) => ref t3; + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(6, 23), + // (7,23): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // public ref T F4A([UnscopedRef] scoped ref T t4) => ref t4; + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(7, 23), + // (14,20): error CS8347: Cannot use a result of 'A.F1A(ref int)' in this context because it may expose variables referenced by parameter 't1' outside of their declaration scope + // return ref F1A(ref i); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "F1A(ref i)").WithArguments("A.F1A(ref int)", "t1").WithLocation(14, 20), + // (14,28): error CS8168: Cannot return local 'i' by reference because it is not a ref local + // return ref F1A(ref i); // 1 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(14, 28), + // (24,20): error CS8347: Cannot use a result of 'A.F3A(ref int)' in this context because it may expose variables referenced by parameter 't3' outside of their declaration scope + // return ref F3A(ref i); // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "F3A(ref i)").WithArguments("A.F3A(ref int)", "t3").WithLocation(24, 20), + // (24,28): error CS8168: Cannot return local 'i' by reference because it is not a ref local + // return ref F3A(ref i); // 2 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(24, 28), + // (29,20): error CS8347: Cannot use a result of 'A.F4A(ref int)' in this context because it may expose variables referenced by parameter 't4' outside of their declaration scope + // return ref F4A(ref i); // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "F4A(ref i)").WithArguments("A.F4A(ref int)", "t4").WithLocation(29, 20), + // (29,28): error CS8168: Cannot return local 'i' by reference because it is not a ref local + // return ref F4A(ref i); // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(29, 28)); + + var baseType = comp.GetMember("B").BaseTypeNoUseSiteDiagnostics; + VerifyParameterSymbol(baseType.GetMethod("F1A").Parameters[0], "ref System.Int32 t1", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F2A").Parameters[0], "scoped ref System.Int32 t2", RefKind.Ref, DeclarationScope.RefScoped); + VerifyParameterSymbol(baseType.GetMethod("F3A").Parameters[0], "ref System.Int32 t3", RefKind.Ref, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F4A").Parameters[0], "ref System.Int32 t4", RefKind.Ref, DeclarationScope.Unscoped); + } + + [Fact] + public void UnscopedRefAttribute_InParameter() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +public class A +{ + public ref readonly T F1A(in T t1) => ref t1; + public ref readonly T F2A(scoped in T t2) => throw null; + public ref readonly T F3A([UnscopedRef] in T t3) => ref t3; + public ref readonly T F4A([UnscopedRef] scoped in T t4) => ref t4; +} +class B : A +{ + ref readonly int F1B() + { + int i = 1; + return ref F1A(in i); // 1 + } + ref readonly int F2B() + { + int i = 2; + return ref F2A(in i); + } + ref readonly int F3B() + { + int i = 3; + return ref F3A(in i); // 2 + } + ref readonly int F4B() + { + int i = 4; + return ref F4A(in i); // 3 + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyEmitDiagnostics( + // (6,32): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // public ref readonly T F3A([UnscopedRef] in T t3) => ref t3; + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(6, 32), + // (7,32): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // public ref readonly T F4A([UnscopedRef] scoped in T t4) => ref t4; + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(7, 32), + // (14,20): error CS8347: Cannot use a result of 'A.F1A(in int)' in this context because it may expose variables referenced by parameter 't1' outside of their declaration scope + // return ref F1A(in i); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "F1A(in i)").WithArguments("A.F1A(in int)", "t1").WithLocation(14, 20), + // (14,27): error CS8168: Cannot return local 'i' by reference because it is not a ref local + // return ref F1A(in i); // 1 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(14, 27), + // (24,20): error CS8347: Cannot use a result of 'A.F3A(in int)' in this context because it may expose variables referenced by parameter 't3' outside of their declaration scope + // return ref F3A(in i); // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "F3A(in i)").WithArguments("A.F3A(in int)", "t3").WithLocation(24, 20), + // (24,27): error CS8168: Cannot return local 'i' by reference because it is not a ref local + // return ref F3A(in i); // 2 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(24, 27), + // (29,20): error CS8347: Cannot use a result of 'A.F4A(in int)' in this context because it may expose variables referenced by parameter 't4' outside of their declaration scope + // return ref F4A(in i); // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "F4A(in i)").WithArguments("A.F4A(in int)", "t4").WithLocation(29, 20), + // (29,27): error CS8168: Cannot return local 'i' by reference because it is not a ref local + // return ref F4A(in i); // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(29, 27)); + + var baseType = comp.GetMember("B").BaseTypeNoUseSiteDiagnostics; + VerifyParameterSymbol(baseType.GetMethod("F1A").Parameters[0], "in System.Int32 t1", RefKind.In, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F2A").Parameters[0], "scoped in System.Int32 t2", RefKind.In, DeclarationScope.RefScoped); + VerifyParameterSymbol(baseType.GetMethod("F3A").Parameters[0], "in System.Int32 t3", RefKind.In, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F4A").Parameters[0], "in System.Int32 t4", RefKind.In, DeclarationScope.Unscoped); + } + + [Fact] + public void UnscopedRefAttribute_RefStructParameter() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +public ref struct R +{ + public ref T F; + public R(ref T t) { F = ref t; } +} +public class A +{ + public ref T F1A(R r1) + { + return ref r1.F; + } + public ref T F2A(scoped R r2) + { + throw null; + } + public ref T F3A([UnscopedRef] R r3) + { + return ref r3.F; + } + public ref T F4A([UnscopedRef] scoped R r4) + { + return ref r4.F; + } +} +class B : A +{ + ref int F1B() + { + int i = 1; + var r = new R(ref i); + return ref F1A(r); // 1 + } + ref int F2B() + { + int i = 2; + var r = new R(ref i); + return ref F2A(r); + } + ref int F3B() + { + int i = 3; + var r = new R(ref i); + return ref F3A(r); // 2 + } + ref int F4B() + { + int i = 4; + var r = new R(ref i); + return ref F4A(r); // 3 + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyEmitDiagnostics( + // (17,23): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // public ref T F3A([UnscopedRef] R r3) + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(17, 23), + // (21,23): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // public ref T F4A([UnscopedRef] scoped R r4) + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(21, 23), + // (32,20): error CS8347: Cannot use a result of 'A.F1A(R)' in this context because it may expose variables referenced by parameter 'r1' outside of their declaration scope + // return ref F1A(r); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "F1A(r)").WithArguments("A.F1A(R)", "r1").WithLocation(32, 20), + // (32,24): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope + // return ref F1A(r); // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(32, 24), + // (44,20): error CS8347: Cannot use a result of 'A.F3A(R)' in this context because it may expose variables referenced by parameter 'r3' outside of their declaration scope + // return ref F3A(r); // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "F3A(r)").WithArguments("A.F3A(R)", "r3").WithLocation(44, 20), + // (44,24): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope + // return ref F3A(r); // 2 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(44, 24), + // (50,20): error CS8347: Cannot use a result of 'A.F4A(R)' in this context because it may expose variables referenced by parameter 'r4' outside of their declaration scope + // return ref F4A(r); // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "F4A(r)").WithArguments("A.F4A(R)", "r4").WithLocation(50, 20), + // (50,24): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope + // return ref F4A(r); // 3 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(50, 24)); + + var baseType = comp.GetMember("B").BaseTypeNoUseSiteDiagnostics; + VerifyParameterSymbol(baseType.GetMethod("F1A").Parameters[0], "R r1", RefKind.None, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F2A").Parameters[0], "scoped R r2", RefKind.None, DeclarationScope.ValueScoped); + VerifyParameterSymbol(baseType.GetMethod("F3A").Parameters[0], "R r3", RefKind.None, DeclarationScope.Unscoped); + VerifyParameterSymbol(baseType.GetMethod("F4A").Parameters[0], "R r4", RefKind.None, DeclarationScope.Unscoped); + } + + [Fact] + public void UnscopedRefAttribute_Overrides_01() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +abstract class A +{ + internal abstract void F1(out T t); + internal abstract void F2([UnscopedRef] out T t); +} +class B1 : A +{ + internal override void F1(out int i) { i = 0; } + internal override void F2([UnscopedRef] out int i) { i = 0; } +} +class B2 : A +{ + internal override void F1([UnscopedRef] out int i) { i = 0; } // 1 + internal override void F2(out int i) { i = 0; } // 2 +} +"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + // https://github.com/dotnet/roslyn/issues/62340: Should allow removing [UnscopedRef] rather than reporting error 2. + comp.VerifyDiagnostics( + // (14,28): error CS8987: The 'scoped' modifier of parameter 'i' doesn't match overridden or implemented member. + // internal override void F1([UnscopedRef] out int i) { i = 0; } // 1 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "F1").WithArguments("i").WithLocation(14, 28), + // (15,28): error CS8987: The 'scoped' modifier of parameter 'i' doesn't match overridden or implemented member. + // internal override void F2(out int i) { i = 0; } // 2 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "F2").WithArguments("i").WithLocation(15, 28)); + } + + [Fact] + public void UnscopedRefAttribute_Overrides_02() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +abstract class A +{ + internal abstract void F1(ref T t); + internal abstract void F2([UnscopedRef] ref T t); +} +class B1 : A +{ + internal override void F1(ref int i) { } + internal override void F2([UnscopedRef] ref int i) { } +} +class B2 : A +{ + internal override void F1([UnscopedRef] ref int i) { } + internal override void F2(ref int i) { } +} +"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (5,32): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // internal abstract void F2([UnscopedRef] ref T t); + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(5, 32), + // (10,32): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // internal override void F2([UnscopedRef] ref int i) { } + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(10, 32), + // (14,32): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // internal override void F1([UnscopedRef] ref int i) { } + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(14, 32)); + } + + [Fact] + public void UnscopedRefAttribute_Overrides_03() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +ref struct R +{ +} +abstract class A +{ + internal abstract void F1(ref R r); + internal abstract void F2([UnscopedRef] ref R r); +} +class B1 : A +{ + internal override void F1(ref R r) { } + internal override void F2([UnscopedRef] ref R r) { } +} +class B2 : A +{ + internal override void F1([UnscopedRef] ref R r) { } // 1 + internal override void F2(ref R r) { } // 2 +} +"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + // https://github.com/dotnet/roslyn/issues/62691: Expected errors should be reported when 'ref R' is considered 'scoped ref'. + comp.VerifyDiagnostics(); + } + + [Fact] + public void UnscopedRefAttribute_Implementations_01() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +interface I +{ + void F1(out T t); + void F2([UnscopedRef] out T t); +} +class C1 : I +{ + public void F1(out int i) { i = 0; } + public void F2([UnscopedRef] out int i) { i = 0; } +} +class C2 : I +{ + public void F1([UnscopedRef] out int i) { i = 0; } // 1 + public void F2(out int i) { i = 0; } // 2 +} +class C3 : I +{ + void I.F1(out object o) { o = null; } + void I.F2([UnscopedRef] out object o) { o = null; } +} +class C4 : I +{ + void I.F1([UnscopedRef] out object o) { o = null; } // 3 + void I.F2(out object o) { o = null; } // 4 +} +"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + // https://github.com/dotnet/roslyn/issues/62340: Should allow removing [UnscopedRef] rather than reporting error 2 and 4. + comp.VerifyDiagnostics( + // (14,17): error CS8987: The 'scoped' modifier of parameter 'i' doesn't match overridden or implemented member. + // public void F1([UnscopedRef] out int i) { i = 0; } // 1 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "F1").WithArguments("i").WithLocation(14, 17), + // (15,17): error CS8987: The 'scoped' modifier of parameter 'i' doesn't match overridden or implemented member. + // public void F2(out int i) { i = 0; } // 2 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "F2").WithArguments("i").WithLocation(15, 17), + // (24,20): error CS8987: The 'scoped' modifier of parameter 'o' doesn't match overridden or implemented member. + // void I.F1([UnscopedRef] out object o) { o = null; } // 3 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "F1").WithArguments("o").WithLocation(24, 20), + // (25,20): error CS8987: The 'scoped' modifier of parameter 'o' doesn't match overridden or implemented member. + // void I.F2(out object o) { o = null; } // 4 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "F2").WithArguments("o").WithLocation(25, 20)); + } + + [Fact] + public void UnscopedRefAttribute_Implementations_02() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +interface I +{ + void F1(ref T t); + void F2([UnscopedRef] ref T t); +} +class C1 : I +{ + public void F1(ref int i) { } + public void F2([UnscopedRef] ref int i) { } +} +class C2 : I +{ + public void F1([UnscopedRef] ref int i) { } + public void F2(ref int i) { } +} +class C3 : I +{ + void I.F1(ref object o) { } + void I.F2([UnscopedRef] ref object o) { } +} +class C4 : I +{ + void I.F1([UnscopedRef] ref object o) { } + void I.F2(ref object o) { } +} +"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (5,14): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // void F2([UnscopedRef] ref T t); + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(5, 14), + // (10,21): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // public void F2([UnscopedRef] ref int i) { } + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(10, 21), + // (14,21): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // public void F1([UnscopedRef] ref int i) { } + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(14, 21), + // (20,24): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // void I.F2([UnscopedRef] ref object o) { } + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(20, 24), + // (24,24): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // void I.F1([UnscopedRef] ref object o) { } + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(24, 24)); + } + + [Fact] + public void UnscopedRefAttribute_Implementations_03() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +ref struct R +{ +} +interface I +{ + void F1(ref R r); + void F2([UnscopedRef] ref R r); +} +class C1 : I +{ + public void F1(ref R r) { } + public void F2([UnscopedRef] ref R r) { } +} +class C2 : I +{ + public void F1([UnscopedRef] ref R r) { } // 1 + public void F2(ref R r) { } // 2 +} +class C3 : I +{ + void I.F1(ref R r) { } + void I.F2([UnscopedRef] ref R r) { } +} +class C4 : I +{ + void I.F1([UnscopedRef] ref R r) { } // 3 + void I.F2(ref R r) { } // 4 +} +"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + // https://github.com/dotnet/roslyn/issues/62691: Expected errors should be reported when 'ref R' is considered 'scoped ref'. + comp.VerifyDiagnostics(); + } + + [Fact] + public void UnscopedRefAttribute_Delegates_01() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +delegate void D1(out T t); +delegate void D2([UnscopedRef] out T t); +class Program +{ + static void Main() + { + D1 d1; + d1 = (out int i1) => { i1 = 1; }; + d1 = ([UnscopedRef] out int i2) => { i2 = 2; }; // 1 + D2 d2; + d2 = (out object o1) => { o1 = 1; }; // 2 + d2 = ([UnscopedRef] out object o2) => { o2 = 2; }; + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + // https://github.com/dotnet/roslyn/issues/62340: Should allow removing [UnscopedRef] rather than reporting error 2. + comp.VerifyDiagnostics( + // (10,14): error CS8986: The 'scoped' modifier of parameter 'i2' doesn't match target 'D1'. + // d1 = ([UnscopedRef] out int i2) => { i2 = 2; }; // 1 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "([UnscopedRef] out int i2) => { i2 = 2; }").WithArguments("i2", "D1").WithLocation(10, 14), + // (12,14): error CS8986: The 'scoped' modifier of parameter 'o1' doesn't match target 'D2'. + // d2 = (out object o1) => { o1 = 1; }; // 2 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "(out object o1) => { o1 = 1; }").WithArguments("o1", "D2").WithLocation(12, 14)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var lambdas = tree.GetRoot().DescendantNodes().OfType().Select(e => model.GetSymbolInfo(e).Symbol.GetSymbol()).ToArray(); + + VerifyParameterSymbol(lambdas[0].Parameters[0], "out System.Int32 i1", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(lambdas[1].Parameters[0], "out System.Int32 i2", RefKind.Out, DeclarationScope.Unscoped); + VerifyParameterSymbol(lambdas[2].Parameters[0], "out System.Object o1", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(lambdas[3].Parameters[0], "out System.Object o2", RefKind.Out, DeclarationScope.Unscoped); + } + + [Fact] + public void UnscopedRefAttribute_Delegates_02() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +delegate void D1(ref T t); +delegate void D2([UnscopedRef] ref T t); +class Program +{ + static void Main() + { + D1 d1; + d1 = (ref int i1) => { }; + d1 = ([UnscopedRef] ref int i2) => { }; + D2 d2; + d2 = (ref object o1) => { }; + d2 = ([UnscopedRef] ref object o2) => { }; + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics( + // (3,22): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // delegate void D2([UnscopedRef] ref T t); + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(3, 22), + // (10,16): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // d1 = ([UnscopedRef] ref int i2) => { }; + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(10, 16), + // (13,16): error CS9063: UnscopedRefAttribute can only be applied to 'out' parameters, 'ref' parameters that refer to 'ref struct' types, and instance methods and properties on 'struct' types other than constructors and 'init' accessors. + // d2 = ([UnscopedRef] ref object o2) => { }; + Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(13, 16)); + } + + [Fact] + public void UnscopedRefAttribute_Delegates_03() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +ref struct R { } +delegate void D1(ref R r); +delegate void D2([UnscopedRef] ref R r); +class Program +{ + static void Main() + { + D1 d1; + d1 = (ref R r1) => { }; + d1 = ([UnscopedRef] ref R r2) => { }; // 1 + D2 d2; + d2 = (ref R r1) => { }; // 2 + d2 = ([UnscopedRef] ref R r2) => { }; + } +}"; + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + // https://github.com/dotnet/roslyn/issues/62691: Expected errors should be reported when 'ref R' is considered 'scoped ref'. + comp.VerifyDiagnostics(); + } + + [Fact] + public void UnscopedRefAttribute_Cycle() + { + var source = +@"namespace System.Diagnostics.CodeAnalysis +{ + public sealed class UnscopedRefAttribute : Attribute + { + public UnscopedRefAttribute([UnscopedRef] out int i) { i = 0; } + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (5,38): error CS7036: There is no argument given that corresponds to the required formal parameter 'i' of 'UnscopedRefAttribute.UnscopedRefAttribute(out int)' + // public UnscopedRefAttribute([UnscopedRef] out int i) { i = 0; } + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "UnscopedRef").WithArguments("i", "System.Diagnostics.CodeAnalysis.UnscopedRefAttribute.UnscopedRefAttribute(out int)").WithLocation(5, 38)); + } + + [Fact] + public void UnscopedRefAttribute_UnexpectedConstructorArgument() + { + var sourceA = +@"namespace System.Diagnostics.CodeAnalysis +{ + public sealed class UnscopedRefAttribute : Attribute + { + public UnscopedRefAttribute(bool b) { } + } +}"; + var sourceB = +@"using System.Diagnostics.CodeAnalysis; +class Program +{ + static ref int F1([UnscopedRef] out int i1) + { + i1 = 0; + return ref i1; + } + static ref int F2([UnscopedRef(true)] out int i2) + { + i2 = 0; + return ref i2; + } + static ref int F3() + { + int i3; + return ref F1(out i3); + } + static ref int F4() + { + int i4; + return ref F2(out i4); + } +}"; + var comp = CreateCompilation(new[] { sourceA, sourceB }); + comp.VerifyDiagnostics( + // (4,24): error CS7036: There is no argument given that corresponds to the required formal parameter 'b' of 'UnscopedRefAttribute.UnscopedRefAttribute(bool)' + // static ref int F1([UnscopedRef] out int i1) + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "UnscopedRef").WithArguments("b", "System.Diagnostics.CodeAnalysis.UnscopedRefAttribute.UnscopedRefAttribute(bool)").WithLocation(4, 24), + // (12,20): error CS8166: Cannot return a parameter by reference 'i2' because it is not a ref parameter + // return ref i2; + Diagnostic(ErrorCode.ERR_RefReturnParameter, "i2").WithArguments("i2").WithLocation(12, 20)); + } + + [Fact] + public void UnscopedRefAttribute_InvalidConstructorArgument() + { + var sourceA = +@"namespace System.Diagnostics.CodeAnalysis +{ + public sealed class UnscopedRefAttribute : Attribute + { + public UnscopedRefAttribute(bool b) { } + } +}"; + var sourceB = +@"using System.Diagnostics.CodeAnalysis; +class Program +{ + static bool F1() + { + return true; + } + static ref int F2([UnscopedRef(F1())] out int i) + { + i = 0; + return ref i; + } +}"; + var comp = CreateCompilation(new[] { sourceA, sourceB }); + comp.VerifyDiagnostics( + // (8,36): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // static ref int F2([UnscopedRef(F1())] out int i) + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "F1()").WithLocation(8, 36), + // (11,20): error CS8166: Cannot return a parameter by reference 'i' because it is not a ref parameter + // return ref i; + Diagnostic(ErrorCode.ERR_RefReturnParameter, "i").WithArguments("i").WithLocation(11, 20)); + } + + [Fact] + public void UnscopedRefAttribute_ScopeRefAttribute() + { + var sourceA = +@".class private System.Diagnostics.CodeAnalysis.UnscopedRefAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class private System.Runtime.CompilerServices.LifetimeAnnotationAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor(bool isRefScoped, bool isValueScoped) cil managed { ret } +} +.class public A +{ + .method public static int32& NoAttributes([out] int32& i) + { + ldnull + throw + } + .method public static int32& ScopedRefOnly([out] int32& i) + { + .param [1] + .custom instance void System.Runtime.CompilerServices.LifetimeAnnotationAttribute::.ctor(bool, bool) = ( 01 00 01 00 00 00 ) // LifetimeAnnotationAttribute(isRefScoped: true, isValueScoped: false) + ldnull + throw + } + .method public static int32& UnscopedRefOnly([out] int32& i) + { + .param [1] + .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + .method public static int32& ScopedRefAndUnscopedRef([out] int32& i) + { + .param [1] + .custom instance void System.Diagnostics.CodeAnalysis.UnscopedRefAttribute::.ctor() = ( 01 00 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.LifetimeAnnotationAttribute::.ctor(bool, bool) = ( 01 00 01 00 00 00 ) // LifetimeAnnotationAttribute(isRefScoped: true, isValueScoped: false) + ldnull + throw + } +} +"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class Program +{ + static ref int F1() + { + int i; + return ref A.NoAttributes(out i); + } + static ref int F2() + { + int i; + return ref A.ScopedRefOnly(out i); + } + static ref int F3() + { + int i; + return ref A.UnscopedRefOnly(out i); // 1 + } + static ref int F4() + { + int i; + return ref A.ScopedRefAndUnscopedRef(out i); // 2 + } +}"; + var comp = CreateCompilation(sourceB, new[] { refA }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp); + + var typeA = comp.GetMember("A"); + VerifyParameterSymbol(typeA.GetMethod("NoAttributes").Parameters[0], "out System.Int32 i", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(typeA.GetMethod("ScopedRefOnly").Parameters[0], "out System.Int32 i", RefKind.Out, DeclarationScope.RefScoped); + VerifyParameterSymbol(typeA.GetMethod("UnscopedRefOnly").Parameters[0], "out System.Int32 i", RefKind.Out, DeclarationScope.Unscoped); + VerifyParameterSymbol(typeA.GetMethod("ScopedRefAndUnscopedRef").Parameters[0], "out System.Int32 i", RefKind.Out, DeclarationScope.Unscoped); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index 11b98e31faa0f..acdb596eacbc5 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -8313,6 +8313,32 @@ unsafe class Program "delegate*"); } + [Fact] + public void ScopedParameter_04() + { + var source = +@"using System.Diagnostics.CodeAnalysis; +ref struct R { } +class Program +{ + static void F1(out int i1, [UnscopedRef] out int i2) => throw null; + static void F2(ref R r1, [UnscopedRef] ref R r2) => throw null; +}"; + + var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }); + comp.VerifyDiagnostics(); + + var format = SymbolDisplayFormat.TestFormat. + WithParameterOptions(SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeParamsRefOut). + WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeScoped); + + Verify(comp.GetMember("Program.F1").ToDisplayParts(format), + "void Program.F1(out System.Int32 i1, out System.Int32 i2)"); + + Verify(comp.GetMember("Program.F2").ToDisplayParts(format), + "void Program.F2(ref R r1, ref R r2)"); + } + [Fact] public void ScopedLocal() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 1959c70e93606..c69bc5d933447 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -617,6 +617,7 @@ public void AllWellKnownTypes() case WellKnownType.System_Runtime_CompilerServices_ScopedRefAttribute: case WellKnownType.System_MemoryExtensions: case WellKnownType.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute: + case WellKnownType.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute: // Not yet in the platform. continue; case WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation: @@ -981,6 +982,7 @@ public void AllWellKnownTypeMembers() case WellKnownMember.System_MemoryExtensions__SequenceEqual_ReadOnlySpan_T: case WellKnownMember.System_MemoryExtensions__AsSpan_String: case WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor: + case WellKnownMember.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__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 00c344d3ce050..0591e2e8471c4 100644 --- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs +++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs @@ -1092,6 +1092,11 @@ internal bool HasScopedRefAttribute(EntityHandle token) return FindTargetAttribute(token, AttributeDescription.ScopedRefAttribute).HasValue; } + internal bool HasUnscopedRefAttribute(EntityHandle token) + { + return FindTargetAttribute(token, AttributeDescription.UnscopedRefAttribute).HasValue; + } + internal bool HasTupleElementNamesAttribute(EntityHandle token, out ImmutableArray tupleElementNames) { var info = FindTargetAttribute(token, AttributeDescription.TupleElementNamesAttribute); diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index 205367fae0207..cfe61b5453111 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -477,5 +477,6 @@ static AttributeDescription() internal static readonly AttributeDescription RequiredMemberAttribute = new AttributeDescription("System.Runtime.CompilerServices", "RequiredMemberAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription SetsRequiredMembersAttribute = new AttributeDescription("System.Diagnostics.CodeAnalysis", "SetsRequiredMembersAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription CompilerFeatureRequiredAttribute = new AttributeDescription("System.Runtime.CompilerServices", "CompilerFeatureRequiredAttribute", s_signatures_HasThis_Void_String_Only); + internal static readonly AttributeDescription UnscopedRefAttribute = new AttributeDescription("System.Diagnostics.CodeAnalysis", "UnscopedRefAttribute", s_signatures_HasThis_Void_Only); } } diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 67710436f5cb8..7c993b1e31200 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -529,6 +529,7 @@ internal enum WellKnownMember System_MemoryExtensions__AsSpan_String, System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, + System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor, System_MissingMethodException__ctor, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index ef9135e334a78..42caed3424a60 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -3638,6 +3638,13 @@ static WellKnownMembers() 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, + + // System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor + (byte)MemberFlags.Constructor, // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type // System_MissingMethodException__ctor (byte)MemberFlags.Constructor, // Flags @@ -4101,6 +4108,7 @@ static WellKnownMembers() "SequenceEqual", // System_MemoryExtensions__SequenceEqual_ReadOnlySpan_T "AsSpan", // System_MemoryExtensions__AsSpan_String ".ctor", // System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute_ctor + ".ctor", // System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor ".ctor", // System_MissingMethodException__ctor }; diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 4a51179a65f76..30af781d8f281 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -323,6 +323,7 @@ internal enum WellKnownType System_MemoryExtensions, System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute, + System_Diagnostics_CodeAnalysis_UnscopedRefAttribute, System_MissingMethodException, @@ -640,6 +641,7 @@ internal static class WellKnownTypes "System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute", "System.MemoryExtensions", "System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute", + "System.Diagnostics.CodeAnalysis.UnscopedRefAttribute", "System.MissingMethodException", }; diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 43b4045dd195c..605774c03c366 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -625,6 +625,15 @@ public UnmanagedCallersOnlyAttribute() { } } }"; + protected const string UnscopedRefAttributeDefinition = +@"namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] + public sealed class UnscopedRefAttribute : Attribute + { + } +}"; + protected const string RequiredMemberAttribute = @" namespace System.Runtime.CompilerServices { diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 4877aa66b5003..7532fe56fb447 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -552,6 +552,7 @@ End Namespace WellKnownType.System_MemoryExtensions, WellKnownType.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute, WellKnownType.System_Runtime_CompilerServices_ScopedRefAttribute, + WellKnownType.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute, WellKnownType.System_MemoryExtensions ' Not available on all platforms. Continue For @@ -623,7 +624,8 @@ End Namespace WellKnownType.System_Diagnostics_CodeAnalysis_SetsRequiredMembersAttribute, WellKnownType.System_MemoryExtensions, WellKnownType.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute, - WellKnownType.System_Runtime_CompilerServices_ScopedRefAttribute + WellKnownType.System_Runtime_CompilerServices_ScopedRefAttribute, + WellKnownType.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel @@ -719,7 +721,8 @@ End Namespace WellKnownMember.System_MemoryExtensions__SequenceEqual_Span_T, WellKnownMember.System_MemoryExtensions__SequenceEqual_ReadOnlySpan_T, WellKnownMember.System_MemoryExtensions__AsSpan_String, - WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor + WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, + WellKnownMember.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor ' Not available yet, but will be in upcoming release. Continue For Case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, @@ -871,7 +874,8 @@ End Namespace WellKnownMember.System_MemoryExtensions__SequenceEqual_Span_T, WellKnownMember.System_MemoryExtensions__SequenceEqual_ReadOnlySpan_T, WellKnownMember.System_MemoryExtensions__AsSpan_String, - WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor + WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, + WellKnownMember.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor ' Not available yet, but will be in upcoming release. Continue For Case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index cdb6bb56e93ba..6fb4e0fbc25e5 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -434,6 +434,8 @@ internal override ObsoleteAttributeData ObsoleteAttributeData internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable; + internal override bool HasUnscopedRefAttribute => false; + internal ResultProperties ResultProperties { get { return _lazyResultProperties; } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectIdLocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectIdLocalSymbol.cs index 830898802cdf2..a9a75c0c859ab 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectIdLocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectIdLocalSymbol.cs @@ -77,7 +77,7 @@ internal override BoundExpression GetAddress(BoundPseudoVariable variable) method.Name, m => method.TypeParameters.SelectAsArray(t => (TypeParameterSymbol)new SimpleTypeParameterSymbol(m, t.Ordinal, t.Name)), m => m.TypeParameters[0], // return type is <>T& - m => method.Parameters.SelectAsArray(p => SynthesizedParameterSymbol.Create(m, p.TypeWithAnnotations, p.Ordinal, p.RefKind, p.Name, p.Scope, p.RefCustomModifiers))); + m => method.Parameters.SelectAsArray(p => SynthesizedParameterSymbol.Create(m, p.TypeWithAnnotations, p.Ordinal, p.RefKind, p.Name, p.DeclaredScope, p.RefCustomModifiers))); var local = variable.LocalSymbol; return InvokeGetMethod(method.Construct(local.Type), variable.Syntax, local.Name); } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs index bed4b97588505..b7ba65e36fe68 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs @@ -218,6 +218,8 @@ internal override ObsoleteAttributeData ObsoleteAttributeData internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable; + internal override bool HasUnscopedRefAttribute => false; + internal override bool RequiresSecurityObject { get { return false; }