From 369bc83ac068818f62a3bb069f5ddd37c590e533 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 23 Mar 2022 21:19:14 -0700 Subject: [PATCH 01/29] Support ref field declarations from source and metadata --- .../Portable/Binder/Binder.ValueChecks.cs | 20 +- .../BoundTree/BoundExpressionExtensions.cs | 3 + .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/CodeGen/EmitAddress.cs | 4 +- .../CSharp/Portable/CodeGen/EmitExpression.cs | 50 +- .../CSharp/Portable/CodeGen/EmitStatement.cs | 3 +- .../CSharp/Portable/CodeGen/Optimizer.cs | 5 +- .../Emitter/Model/FieldSymbolAdapter.cs | 5 + .../Model/SpecializedFieldReference.cs | 5 + .../CSharp/Portable/Errors/MessageID.cs | 2 + .../LocalRewriter_AssignmentOperator.cs | 11 +- .../CSharp/Portable/Parser/LanguageParser.cs | 10 +- .../SymbolDisplayVisitor.Members.cs | 12 + .../AnonymousType.FieldSymbol.cs | 4 + .../CSharp/Portable/Symbols/FieldSymbol.cs | 4 + .../Metadata/PE/MemberRefMetadataDecoder.cs | 6 +- .../Symbols/Metadata/PE/PEFieldSymbol.cs | 61 +- .../Symbols/Metadata/PE/PENamedTypeSymbol.cs | 7 +- .../Symbols/PublicModel/FieldSymbol.cs | 4 + .../Retargeting/RetargetingFieldSymbol.cs | 5 + .../FieldSymbolWithAttributesAndModifiers.cs | 5 + .../Source/GlobalExpressionVariable.cs | 2 + .../Source/SourceEnumConstantSymbol.cs | 2 + .../Symbols/Source/SourceFieldSymbol.cs | 9 + .../Symbols/Source/SourceMemberFieldSymbol.cs | 42 +- .../Symbols/SubstitutedFieldSymbol.cs | 5 + .../SynthesizedBackingFieldSymbol.cs | 5 + .../Synthesized/SynthesizedFieldSymbolBase.cs | 4 + .../Symbols/Tuples/TupleFieldSymbol.cs | 4 + .../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 + .../Test/Semantic/Semantics/RefFieldTests.cs | 1891 +++++++++++++++++ .../SymbolDisplay/SymbolDisplayTests.cs | 43 + .../Syntax/Parsing/RefFieldParsingTests.cs | 208 ++ .../Test/Syntax/Parsing/RefReadonlyTests.cs | 17 +- .../CodeGen/PrivateImplementationDetails.cs | 4 + .../Emit/NoPia/CommonEmbeddedField.cs | 4 + .../MetadataReader/MetadataDecoder.cs | 59 +- .../Core/Portable/PEWriter/Members.cs | 13 + .../Core/Portable/PEWriter/MetadataWriter.cs | 6 + .../Core/Portable/PublicAPI.Unshipped.txt | 2 + .../Core/Portable/Symbols/IFieldSymbol.cs | 10 + .../Portable/Emit/FieldSymbolAdapter.vb | 12 + .../Emit/SpecializedFieldReference.vb | 18 +- .../Portable/Symbols/FieldSymbol.vb | 18 + .../Metadata/PE/MemberRefMetadataDecoder.vb | 6 +- .../Symbols/Metadata/PE/PEFieldSymbol.vb | 10 +- .../SymbolDisplay/SymbolDisplayTests.vb | 48 + .../Symbols/DisplayClassVariable.cs | 4 + .../ExpressionCompiler/SyntaxHelpers.cs | 2 +- .../ExpressionCompilerTests.cs | 32 + .../Test/ResultProvider/ExpansionTests.cs | 29 + ...adataAsSourceService.WrappedFieldSymbol.cs | 4 + .../Symbols/CodeGenerationFieldSymbol.cs | 4 + 65 files changed, 2722 insertions(+), 89 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs create mode 100644 src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index b61abacd57cd1..3d533c3b4c0ec 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -829,6 +829,12 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, } } + if (fieldSymbol.RefKind == RefKind.RefReadOnly) + { + ReportReadOnlyError(fieldSymbol, node, valueKind, checkingReceiver, diagnostics); + return false; + } + if (fieldSymbol.IsFixedSizeBuffer) { Error(diagnostics, GetStandardLvalueError(valueKind), node); @@ -838,8 +844,18 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, if (RequiresRefAssignableVariable(valueKind)) { - Error(diagnostics, ErrorCode.ERR_RefLocalOrParamExpected, node); - return false; + switch (fieldSymbol.RefKind) + { + case RefKind.None: + Debug.Assert(fieldSymbol.RefKind == RefKind.None); + Error(diagnostics, ErrorCode.ERR_RefLocalOrParamExpected, node); + return false; + case RefKind.Ref: + case RefKind.RefReadOnly: + return true; + default: + throw ExceptionUtilities.UnexpectedValue(fieldSymbol.RefKind); + } } // r/w fields that are static or belong to reference types are writeable and returnable diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs index 34d94060b1d9c..e91703598a061 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs @@ -26,6 +26,9 @@ public static RefKind GetRefKind(this BoundExpression node) case BoundKind.Parameter: return ((BoundParameter)node).ParameterSymbol.RefKind; + case BoundKind.FieldAccess: + return ((BoundFieldAccess)node).FieldSymbol.RefKind; + case BoundKind.Call: return ((BoundCall)node).Method.RefKind; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 3abfac0e83f71..c5408fa46211a 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6642,6 +6642,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ struct field initializers + + ref fields + variance safety for static interface members diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs index 8ac52b47e4f9e..e6c9da8ea9f2f 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs @@ -345,7 +345,7 @@ private LocalDefinition EmitSequenceAddress(BoundSequence sequence, AddressKind return result; } - private LocalSymbol DigForValueLocal(BoundSequence topSequence, BoundExpression value) + private static LocalSymbol DigForValueLocal(BoundSequence topSequence, BoundExpression value) { switch (value.Kind) { @@ -546,7 +546,7 @@ private LocalDefinition EmitInstanceFieldAddress(BoundFieldAccess fieldAccess, A // taking field addresses, so we have to turn Constrained into writeable. var tempOpt = EmitReceiverRef(fieldAccess.ReceiverOpt, addressKind == AddressKind.Constrained ? AddressKind.Writeable : addressKind); - _builder.EmitOpCode(ILOpCode.Ldflda); + _builder.EmitOpCode(field.RefKind == RefKind.None ? ILOpCode.Ldflda : ILOpCode.Ldfld); EmitSymbolToken(field, fieldAccess.Syntax); // when loading an address of a fixed field, we actually diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index f684af2db356c..39b76956cf95b 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -1023,6 +1023,20 @@ private void EmitFieldLoad(BoundFieldAccess fieldAccess, bool used) Debug.Assert(!field.IsConst || field.ContainingType.SpecialType == SpecialType.System_Decimal, "rewriter should lower constant fields into constant expressions"); + EmitFieldLoadNoIndirection(fieldAccess, used); + + if (used && field.RefKind != RefKind.None) + { + EmitLoadIndirect(field.Type, fieldAccess.Syntax); + } + + EmitPopIfUnused(used); + } + + private void EmitFieldLoadNoIndirection(BoundFieldAccess fieldAccess, bool used) + { + var field = fieldAccess.FieldSymbol; + // static field access is sideeffecting since it guarantees that ..ctor has run. // we emit static accesses even if unused. if (field.IsStatic) @@ -1062,7 +1076,6 @@ private void EmitFieldLoad(BoundFieldAccess fieldAccess, bool used) EmitSymbolToken(field, fieldAccess.Syntax); } } - EmitPopIfUnused(used); } private LocalDefinition EmitFieldLoadReceiver(BoundExpression receiver) @@ -1128,10 +1141,10 @@ private bool EmitFieldLoadReceiverAddress(BoundExpression receiver) return false; } - // ldfld can work with structs directly or with their addresses + // ldfld can work with structs directly or with their addresses. // In some cases it results in same native code emitted, but in some cases JIT pushes values for real // resulting in much worse code (on x64 in particular). - // So, we will always prefer references here except when receiver is a struct non-ref local or parameter. + // So, we will always prefer references here except when receiver is a struct, non-ref local, or parameter. private bool FieldLoadPrefersRef(BoundExpression receiver) { // only fields of structs can be accessed via value @@ -2089,7 +2102,7 @@ private void EmitAssignmentExpression(BoundAssignmentOperator assignmentOperator EmitAssignmentPostfix(assignmentOperator, temp, useKind); } - // sometimes it is possible and advantageous to get an address of the lHS and + // sometimes it is possible and advantageous to get an address of the LHS and // perform assignment as an in-place initialization via initobj or constructor invocation. // // 1) initobj @@ -2309,10 +2322,15 @@ private bool EmitAssignmentPreamble(BoundAssignmentOperator assignmentOperator) case BoundKind.FieldAccess: { var left = (BoundFieldAccess)assignmentTarget; - if (!left.FieldSymbol.IsStatic) + if (left.FieldSymbol.RefKind != RefKind.None && + !assignmentOperator.IsRef) + { + EmitFieldLoadNoIndirection(left, used: true); + } + else if (!left.FieldSymbol.IsStatic) { var temp = EmitReceiverRef(left.ReceiverOpt, AddressKind.Writeable); - Debug.Assert(temp == null, "temp is unexpected when assigning to a field"); + FreeOptTemp(temp); lhsUsesStack = true; } } @@ -2586,7 +2604,7 @@ private void EmitStore(BoundAssignmentOperator assignment) switch (expression.Kind) { case BoundKind.FieldAccess: - EmitFieldStore((BoundFieldAccess)expression); + EmitFieldStore((BoundFieldAccess)expression, assignment.IsRef); break; case BoundKind.Local: @@ -2794,7 +2812,7 @@ private void EmitVectorElementStore(ArrayTypeSymbol arrayType, SyntaxNode syntax } } - private void EmitFieldStore(BoundFieldAccess fieldAccess) + private void EmitFieldStore(BoundFieldAccess fieldAccess, bool refAssign) { var field = fieldAccess.FieldSymbol; @@ -2803,14 +2821,21 @@ private void EmitFieldStore(BoundFieldAccess fieldAccess) _builder.EmitOpCode(ILOpCode.Volatile); } - _builder.EmitOpCode(field.IsStatic ? ILOpCode.Stsfld : ILOpCode.Stfld); - EmitSymbolToken(field, fieldAccess.Syntax); + if (field.RefKind != RefKind.None && !refAssign) + { + //NOTE: we should have the actual field already loaded, + //now need to do a store to where it points to + EmitIndirectStore(field.Type, fieldAccess.Syntax); + } + else + { + _builder.EmitOpCode(field.IsStatic ? ILOpCode.Stsfld : ILOpCode.Stfld); + EmitSymbolToken(field, fieldAccess.Syntax); + } } private void EmitParameterStore(BoundParameter parameter, bool refAssign) { - int slot = ParameterSlot(parameter); - if (parameter.ParameterSymbol.RefKind != RefKind.None && !refAssign) { //NOTE: we should have the actual parameter already loaded, @@ -2819,6 +2844,7 @@ private void EmitParameterStore(BoundParameter parameter, bool refAssign) } else { + int slot = ParameterSlot(parameter); _builder.EmitStoreArgumentOpcode(slot); } } diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs index 2908e3983433d..8e1be7fbd5144 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs @@ -1034,6 +1034,7 @@ private void EmitCatchBlock(BoundCatchBlock catchBlock) var left = (BoundFieldAccess)exceptionSource; Debug.Assert(!left.FieldSymbol.IsStatic, "Not supported"); Debug.Assert(!left.ReceiverOpt.Type.IsTypeParameter()); + Debug.Assert(left.FieldSymbol.RefKind == RefKind.None); var stateMachineField = left.FieldSymbol as StateMachineFieldSymbol; if (((object)stateMachineField != null) && (stateMachineField.SlotIndex >= 0)) @@ -1052,7 +1053,7 @@ private void EmitCatchBlock(BoundCatchBlock catchBlock) _builder.EmitLocalLoad(temp); FreeTemp(temp); - EmitFieldStore(left); + EmitFieldStore(left, refAssign: false); break; default: diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs index 0c6ba8d2c5d56..b6efd8b6901fb 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs @@ -1022,8 +1022,7 @@ private static bool IsIndirectAssignment(BoundAssignmentOperator node) var lhs = node.Left; Debug.Assert(!node.IsRef || - (lhs is BoundLocal local && local.LocalSymbol.RefKind != RefKind.None) || - (lhs is BoundParameter param && param.ParameterSymbol.RefKind != RefKind.None), + (lhs.Kind is BoundKind.Local or BoundKind.Parameter or BoundKind.FieldAccess && lhs.GetRefKind() != RefKind.None), "only ref symbols can be a target of a ref assignment"); switch (lhs.Kind) @@ -2055,7 +2054,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) } // indirect local store is not special. (operands still could be rewritten) - // NOTE: if Lhs is a stack local, it will be handled as a read and possibly duped. + // NOTE: if lhs is a stack local, it will be handled as a read and possibly duped. var isIndirectLocalStore = left.LocalSymbol.RefKind != RefKind.None && !node.IsRef; if (isIndirectLocalStore) { diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/FieldSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/FieldSymbolAdapter.cs index 78c10edb9912a..fdb3762ede5bd 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/FieldSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/FieldSymbolAdapter.cs @@ -47,6 +47,11 @@ Cci.ITypeReference Cci.IFieldReference.GetType(EmitContext context) } } + ImmutableArray Cci.IFieldReference.RefCustomModifiers => + ImmutableArray.CastUp(AdaptedFieldSymbol.RefCustomModifiers); + + bool Cci.IFieldReference.IsByReference => AdaptedFieldSymbol.RefKind != RefKind.None; + Cci.IFieldDefinition Cci.IFieldReference.GetResolvedField(EmitContext context) { return ResolvedFieldImpl((PEModuleBuilder)context.Module); diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/SpecializedFieldReference.cs b/src/Compilers/CSharp/Portable/Emitter/Model/SpecializedFieldReference.cs index 3952875053d5a..ec96a3aa8613a 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/SpecializedFieldReference.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/SpecializedFieldReference.cs @@ -74,6 +74,11 @@ Cci.ITypeReference Cci.IFieldReference.GetType(EmitContext context) } } + ImmutableArray Cci.IFieldReference.RefCustomModifiers => + ImmutableArray.CastUp(_underlyingField.RefCustomModifiers); + + bool Cci.IFieldReference.IsByReference => _underlyingField.RefKind != RefKind.None; + Cci.IFieldDefinition Cci.IFieldReference.GetResolvedField(EmitContext context) { return null; diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index ffa2463e8c13d..5e4841b3ec838 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -245,6 +245,7 @@ internal enum MessageID IDS_FeatureCacheStaticMethodGroupConversion = MessageBase + 12816, IDS_FeatureRawStringLiterals = MessageBase + 12817, IDS_FeatureDisposalPattern = MessageBase + 12818, + IDS_FeatureRefFields = MessageBase + 12819, } // Message IDs may refer to strings that need to be localized. @@ -361,6 +362,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureListPattern: // semantic check case MessageID.IDS_FeatureCacheStaticMethodGroupConversion: // lowering check case MessageID.IDS_ParameterNullChecking: // syntax check + case MessageID.IDS_FeatureRefFields: // semantic check return LanguageVersion.Preview; // C# 10.0 features. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index cc988be9fa1c0..654c0671d6cf6 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -221,17 +221,8 @@ private BoundExpression MakeStaticAssignmentOperator( } case BoundKind.Local: - { - Debug.Assert(!isRef || ((BoundLocal)rewrittenLeft).LocalSymbol.RefKind != RefKind.None); - return new BoundAssignmentOperator( - syntax, - rewrittenLeft, - rewrittenRight, - type, - isRef: isRef); - } - case BoundKind.Parameter: + case BoundKind.FieldAccess: { Debug.Assert(!isRef || rewrittenLeft.GetRefKind() != RefKind.None); return new BoundAssignmentOperator( diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index b92860f4fa14b..2248ed5abc74f 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -2874,7 +2874,7 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind parse_member_name:; ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt; - // If we've seen the ref keyword, we know we must have an indexer, method, or property. + // If we've seen the ref keyword, we know we must have an indexer, method, field, or property. if (type.Kind != SyntaxKind.RefType) { // Check here for operators @@ -2883,11 +2883,11 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind { return this.ParseOperatorDeclaration(attributes, modifiers, type, explicitInterfaceOpt); } + } - if (IsFieldDeclaration(isEvent: false)) - { - return this.ParseNormalFieldDeclaration(attributes, modifiers, type, parentKind); - } + if (IsFieldDeclaration(isEvent: false)) + { + return this.ParseNormalFieldDeclaration(attributes, modifiers, type, parentKind); } // At this point we can either have indexers, methods, or diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 5b806987fc8cc..989efe828c1a0 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -35,6 +35,18 @@ public override void VisitField(IFieldSymbol symbol) this.isFirstSymbolVisited && !IsEnumMember(symbol)) { + switch (symbol.RefKind) + { + case RefKind.Ref: + AddRefIfRequired(); + break; + case RefKind.RefReadOnly: + AddRefReadonlyIfRequired(); + break; + } + + AddCustomModifiersIfRequired(symbol.RefCustomModifiers); + VisitFieldType(symbol); AddSpace(); diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.FieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.FieldSymbol.cs index 10010ac113676..2088138bf8116 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.FieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.FieldSymbol.cs @@ -32,6 +32,10 @@ internal override TypeWithAnnotations GetFieldType(ConsList fieldsB return _property.TypeWithAnnotations; } + public override RefKind RefKind => RefKind.None; + + public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + public override string Name { get { return GeneratedNames.MakeAnonymousTypeBackingFieldName(_property.Name); } diff --git a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs index b0b29af001f44..9df43536b764b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs @@ -63,6 +63,10 @@ public TypeWithAnnotations TypeWithAnnotations } } + public abstract RefKind RefKind { get; } + + public abstract ImmutableArray RefCustomModifiers { get; } + public abstract FlowAnalysisAnnotations FlowAnalysisAnnotations { get; } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs index 027115bf4bc27..b4679a94d94b9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs @@ -138,9 +138,9 @@ internal Symbol FindMember(MemberReferenceHandle memberRef, bool methodsOnly) return null; } - ImmutableArray> customModifiers; - TypeSymbol type = this.DecodeFieldSignature(ref signaturePointer, out customModifiers); - return FindFieldBySignature(_containingType, memberName, customModifiers, type); + FieldInfo fieldInfo; + this.DecodeFieldSignature(ref signaturePointer, out fieldInfo); + return FindFieldBySignature(_containingType, memberName, fieldInfo.CustomModifiers, fieldInfo.Type); default: // error: unexpected calling convention diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs index 6895447a75757..27e3a2eab6d8f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs @@ -29,15 +29,20 @@ internal sealed class PEFieldSymbol : FieldSymbol private struct PackedFlags { // Layout: - // |..............................|vvvvv| + // |..........................|rr|v|fffff| // // f = FlowAnalysisAnnotations. 5 bits (4 value bits + 1 completion bit). + // v = IsVolatile 1 bit + // r = RefKind 2 bits private const int HasDisallowNullAttribute = 0x1 << 0; private const int HasAllowNullAttribute = 0x1 << 1; private const int HasMaybeNullAttribute = 0x1 << 2; private const int HasNotNullAttribute = 0x1 << 3; private const int FlowAnalysisAnnotationsCompletionBit = 0x1 << 4; + private const int IsVolatileBit = 0x1 << 5; + private const int RefKindOffset = 6; + private const int RefKindMask = 0x3; private int _bits; @@ -67,13 +72,29 @@ public bool TryGetFlowAnalysisAnnotations(out FlowAnalysisAnnotations value) Debug.Assert(value == 0 || result); return result; } + + public void SetIsVolatile(bool isVolatile) + { + if (isVolatile) ThreadSafeFlagOperations.Set(ref _bits, IsVolatileBit); + Debug.Assert(IsVolatile == isVolatile); + } + + public bool IsVolatile => (_bits & IsVolatileBit) != 0; + + public void SetRefKind(RefKind refKind) + { + int bits = ((int)refKind & RefKindMask) << RefKindOffset; + if (bits != 0) ThreadSafeFlagOperations.Set(ref _bits, bits); + Debug.Assert(RefKind == refKind); + } + + public RefKind RefKind => (RefKind)((_bits >> RefKindOffset) & RefKindMask); } private readonly FieldDefinitionHandle _handle; private readonly string _name; private readonly FieldAttributes _flags; private readonly PENamedTypeSymbol _containingType; - private bool _lazyIsVolatile; private ImmutableArray _lazyCustomAttributes; private ConstantValue _lazyConstantValue = Microsoft.CodeAnalysis.ConstantValue.Unset; // Indicates an uninitialized ConstantValue private Tuple _lazyDocComment; @@ -86,6 +107,7 @@ public bool TryGetFlowAnalysisAnnotations(out FlowAnalysisAnnotations value) private NamedTypeSymbol _lazyFixedImplementationType; private PEEventSymbol _associatedEventOpt; private PackedFlags _packedFlags; + private ImmutableArray _lazyRefCustomModifiers; internal PEFieldSymbol( PEModuleSymbol moduleSymbol, @@ -261,9 +283,10 @@ private void EnsureSignatureIsLoaded() if (_lazyType == null) { var moduleSymbol = _containingType.ContainingPEModule; - ImmutableArray> customModifiers; - TypeSymbol typeSymbol = (new MetadataDecoder(moduleSymbol, _containingType)).DecodeFieldSignature(_handle, out customModifiers); - ImmutableArray customModifiersArray = CSharpCustomModifier.Convert(customModifiers); + FieldInfo fieldInfo; + (new MetadataDecoder(moduleSymbol, _containingType)).DecodeFieldSignature(_handle, out fieldInfo); + TypeSymbol typeSymbol = fieldInfo.Type; + ImmutableArray customModifiersArray = CSharpCustomModifier.Convert(fieldInfo.CustomModifiers); typeSymbol = DynamicTypeDecoder.TransformType(typeSymbol, customModifiersArray.Length, _handle, moduleSymbol); typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, _handle, moduleSymbol); @@ -276,7 +299,11 @@ private void EnsureSignatureIsLoaded() type = NullableTypeDecoder.TransformType(type, _handle, moduleSymbol, accessSymbol: this, nullableContext: _containingType); type = TupleTypeDecoder.DecodeTupleTypesIfApplicable(type, _handle, moduleSymbol); - _lazyIsVolatile = customModifiersArray.Any(m => !m.IsOptional && ((CSharpCustomModifier)m).ModifierSymbol.SpecialType == SpecialType.System_Runtime_CompilerServices_IsVolatile); + RefKind refKind = fieldInfo.IsByRef ? + moduleSymbol.Module.HasIsReadOnlyAttribute(_handle) ? RefKind.RefReadOnly : RefKind.Ref : + RefKind.None; + _packedFlags.SetRefKind(refKind); + _packedFlags.SetIsVolatile(customModifiersArray.Any(m => !m.IsOptional && ((CSharpCustomModifier)m).ModifierSymbol.SpecialType == SpecialType.System_Runtime_CompilerServices_IsVolatile)); TypeSymbol fixedElementType; int fixedSize; @@ -287,6 +314,8 @@ private void EnsureSignatureIsLoaded() type = TypeWithAnnotations.Create(new PointerTypeSymbol(TypeWithAnnotations.Create(fixedElementType))); } + ImmutableInterlocked.InterlockedInitialize(ref _lazyRefCustomModifiers, CSharpCustomModifier.Convert(fieldInfo.RefCustomModifiers)); + Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type), null); } } @@ -322,6 +351,24 @@ private PEModuleSymbol ContainingPEModule } } + public override RefKind RefKind + { + get + { + EnsureSignatureIsLoaded(); + return _packedFlags.RefKind; + } + } + + public override ImmutableArray RefCustomModifiers + { + get + { + EnsureSignatureIsLoaded(); + return _lazyRefCustomModifiers; + } + } + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { EnsureSignatureIsLoaded(); @@ -397,7 +444,7 @@ public override bool IsVolatile get { EnsureSignatureIsLoaded(); - return _lazyIsVolatile; + return _packedFlags.IsVolatile; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index df6df4a607427..dff15bddbac2a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -1151,10 +1151,11 @@ private void EnsureEnumUnderlyingTypeIsLoaded(UncommonProperties uncommon) if ((fieldFlags & FieldAttributes.Static) == 0) { // Instance field used to determine underlying type. - ImmutableArray> customModifiers; - TypeSymbol type = decoder.DecodeFieldSignature(fieldDef, out customModifiers); + FieldInfo fieldInfo; + decoder.DecodeFieldSignature(fieldDef, out fieldInfo); + TypeSymbol type = fieldInfo.Type; - if (type.SpecialType.IsValidEnumUnderlyingType() && !customModifiers.AnyRequired()) + if (type.SpecialType.IsValidEnumUnderlyingType() && !fieldInfo.CustomModifiers.AnyRequired()) { if ((object)underlyingType == null) { diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/FieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/FieldSymbol.cs index e277d95d12fd5..cb9ee2caa91b3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/FieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/FieldSymbol.cs @@ -31,6 +31,10 @@ ISymbol IFieldSymbol.AssociatedSymbol } } + RefKind IFieldSymbol.RefKind => _underlying.RefKind; + + ImmutableArray IFieldSymbol.RefCustomModifiers => _underlying.RefCustomModifiers; + ITypeSymbol IFieldSymbol.Type { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingFieldSymbol.cs index a087f842c7370..68f9f0b5a1277 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingFieldSymbol.cs @@ -70,6 +70,11 @@ public override Symbol ContainingSymbol } } + public override RefKind RefKind => _underlyingField.RefKind; + + public override ImmutableArray RefCustomModifiers => + this.RetargetingTranslator.RetargetModifiers(_underlyingField.RefCustomModifiers, out _); + public override ImmutableArray GetAttributes() { return this.RetargetingTranslator.GetRetargetedAttributes(_underlyingField.GetAttributes(), ref _lazyCustomAttributes); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs index e9d21aff2101a..62a5f753e9780 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs @@ -382,6 +382,11 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + if (this.RefKind == RefKind.RefReadOnly) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); + } + var compilation = this.DeclaringCompilation; var type = this.TypeWithAnnotations; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs index 4eb05ee871838..47deee5399c82 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs @@ -64,6 +64,8 @@ protected override ConstantValue MakeConstantValue( bool earlyDecodingWellKnownAttributes, BindingDiagnosticBag diagnostics) => null; + public sealed override RefKind RefKind => RefKind.None; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { Debug.Assert(fieldsBeingBound != null); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs index 79f64f81d2439..d7d45bf347a93 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs @@ -56,6 +56,8 @@ protected SourceEnumConstantSymbol(SourceMemberContainerTypeSymbol containingEnu } } + public sealed override RefKind RefKind => RefKind.None; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { return TypeWithAnnotations.Create(this.ContainingType); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs index 08ab93bd28469..4fa611fb7de63 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs @@ -94,6 +94,10 @@ protected ImmutableArray RequiredCustomModifiers } } + // Currently, source symbols cannot declare RefCustomModifiers. If that changes, and this + // property is updated, test retargeting. (Update RefFieldTests.RetargetingField for instance.) + public sealed override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + public sealed override Symbol ContainingSymbol { get @@ -135,6 +139,11 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, var compilation = DeclaringCompilation; var location = ErrorLocation; + if (RefKind == RefKind.RefReadOnly) + { + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + } + if (Type.ContainsNativeInteger()) { compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index b2b6cdf5584d1..8129a3fe7eb9e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -279,7 +279,19 @@ internal class SourceMemberFieldSymbolFromDeclarator : SourceMemberFieldSymbol { private readonly bool _hasInitializer; - private TypeWithAnnotations.Boxed _lazyType; + private sealed class TypeAndRefKind + { + internal readonly RefKind RefKind; + internal readonly TypeWithAnnotations Type; + + internal TypeAndRefKind(RefKind refKind, TypeWithAnnotations type) + { + RefKind = refKind; + Type = type; + } + } + + private TypeAndRefKind _lazyTypeAndRefKind; // Non-zero if the type of the field has been inferred from the type of its initializer expression // and the errors of binding the initializer have been or are being reported to compilation diagnostics. @@ -367,13 +379,15 @@ protected override SyntaxList AttributeDeclarationSyntaxLis } } + public sealed override RefKind RefKind => GetTypeAndRefKind(ConsList.Empty).RefKind; + internal override bool HasPointerType { get { - if (_lazyType != null) + if (_lazyTypeAndRefKind?.Type.DefaultType is { } defaultType) { - bool isPointerType = _lazyType.Value.DefaultType.Kind switch + bool isPointerType = defaultType.Kind switch { SymbolKind.PointerType => true, SymbolKind.FunctionPointerType => true, @@ -400,12 +414,17 @@ private bool IsPointerFieldSyntactically() } internal sealed override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) + { + return GetTypeAndRefKind(fieldsBeingBound).Type; + } + + private TypeAndRefKind GetTypeAndRefKind(ConsList fieldsBeingBound) { Debug.Assert(fieldsBeingBound != null); - if (_lazyType != null) + if (_lazyTypeAndRefKind != null) { - return _lazyType.Value; + return _lazyTypeAndRefKind; } var declarator = VariableDeclaratorNode; @@ -415,6 +434,7 @@ internal sealed override TypeWithAnnotations GetFieldType(ConsList var compilation = this.DeclaringCompilation; var diagnostics = BindingDiagnosticBag.GetInstance(); + RefKind refKind = RefKind.None; TypeWithAnnotations type; // When we have multiple declarators, we report the type diagnostics on only the first. @@ -446,7 +466,13 @@ internal sealed override TypeWithAnnotations GetFieldType(ConsList binder = binder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); if (!ContainingType.IsScriptClass) { - type = binder.BindType(typeSyntax, diagnosticsForFirstDeclarator); + var typeOnly = typeSyntax.SkipRef(out refKind); + Debug.Assert(refKind is RefKind.None or RefKind.Ref or RefKind.RefReadOnly); + if (refKind != RefKind.None) + { + MessageID.IDS_FeatureRefFields.CheckFeatureAvailability(diagnostics, compilation, typeSyntax.Location); + } + type = binder.BindType(typeOnly, diagnosticsForFirstDeclarator); } else { @@ -530,7 +556,7 @@ internal sealed override TypeWithAnnotations GetFieldType(ConsList Debug.Assert(type.DefaultType.IsPointerOrFunctionPointer() == IsPointerFieldSyntactically()); // update the lazyType only if it contains value last seen by the current thread: - if (Interlocked.CompareExchange(ref _lazyType, new TypeWithAnnotations.Boxed(type.WithModifiers(this.RequiredCustomModifiers)), null) == null) + if (Interlocked.CompareExchange(ref _lazyTypeAndRefKind, new TypeAndRefKind(refKind, type.WithModifiers(this.RequiredCustomModifiers)), null) == null) { TypeChecks(type.Type, diagnostics); @@ -548,7 +574,7 @@ internal sealed override TypeWithAnnotations GetFieldType(ConsList diagnostics.Free(); diagnosticsForFirstDeclarator.Free(); - return _lazyType.Value; + return _lazyTypeAndRefKind; } internal bool FieldTypeInferred(ConsList fieldsBeingBound) diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs index d957d20bfbe9c..0fb488352ade3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs @@ -103,6 +103,11 @@ internal override NamedTypeSymbol FixedImplementationType(PEModuleBuilder emitMo return (NamedTypeSymbol)_containingType.TypeSubstitution.SubstituteType(OriginalDefinition.FixedImplementationType(emitModule)).Type; } + public override RefKind RefKind => _underlyingField.RefKind; + + public override ImmutableArray RefCustomModifiers => + _containingType.TypeSubstitution.SubstituteCustomModifiers(_underlyingField.RefCustomModifiers); + public override bool Equals(Symbol obj, TypeCompareKind compareKind) { if ((object)this == obj) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs index 6001d8b361c51..635eec12c26b7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs @@ -31,6 +31,7 @@ public SynthesizedBackingFieldSymbol( bool hasInitializer) { Debug.Assert(!string.IsNullOrEmpty(name)); + Debug.Assert(property.RefKind is RefKind.None or RefKind.Ref or RefKind.RefReadOnly); _name = name; @@ -57,6 +58,10 @@ public override Symbol AssociatedSymbol public override ImmutableArray Locations => _property.Locations; + public override RefKind RefKind => _property.RefKind; + + public override ImmutableArray RefCustomModifiers => _property.RefCustomModifiers; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) => _property.TypeWithAnnotations; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs index 73a6dacbcf7ce..7355017307232 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs @@ -86,6 +86,10 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r internal abstract override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound); + public sealed override RefKind RefKind => RefKind.None; + + public sealed override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs index 44a1763685642..2ed15d863ba7c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs @@ -121,6 +121,10 @@ public sealed override Symbol ContainingSymbol } } + public sealed override RefKind RefKind => RefKind.None; + + public sealed override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { return _underlyingField.GetFieldType(fieldsBeingBound); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index f68dcd3be5136..4196abcb9fbec 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1482,6 +1482,11 @@ struktury záznamů 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record zapečetěný ToString v záznamu diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 508a996c17c0b..ea3d8c9d865c5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1482,6 +1482,11 @@ Datensatzstrukturen 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record versiegelte "ToString" im Datensatz diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 2908ff4afb81a..17cac02bfb70f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1482,6 +1482,11 @@ registros 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record ToString sellado en el registro diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index bbca3ed9d5fdd..7dba4dfa3e488 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1482,6 +1482,11 @@ structs d’enregistrement 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record ToString scellé dans l’enregistrement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index cbab7dce34dbc..52fe2b9f2b6cc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1482,6 +1482,11 @@ struct di record 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record ToString sealed nel record diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 794537811a0e8..f4918e2d875c3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1482,6 +1482,11 @@ レコード構造体 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record レコードでシールされた ToString diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 6e2f95eade78b..42bbe0e3fe2e1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1482,6 +1482,11 @@ 레코드 구조체 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record 레코드의 봉인된 ToString diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index c18e47055be97..b093e2e5a6ebc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1482,6 +1482,11 @@ struktury rekordów 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record zapieczętowany obiekt ToString w rekordzie diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index fd93d2f204ded..4592073c49f76 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1482,6 +1482,11 @@ registrar structs 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record ToString selado no registro diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 1a91c5e5625ec..0c0a1b4fb69ec 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1482,6 +1482,11 @@ структуры записей 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record запечатанный ToString в записи diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 9f7b4fb609c52..4b0410d40011d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1482,6 +1482,11 @@ kayıt yapıları 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record kayıtta mühürlü ToString diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 01d54adcd19b0..2b33dbfc20ed1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1482,6 +1482,11 @@ 记录结构 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record 记录的密封 ToString diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 85cdecf84ae38..e3dc711f68e6d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1482,6 +1482,11 @@ 記錄結構 'record structs' is not localizable. + + ref fields + ref fields + + sealed ToString in record 記錄中有密封的 ToString diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs new file mode 100644 index 0000000000000..175be0159d53e --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -0,0 +1,1891 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class RefFieldTests : CSharpTestBase + { + private static string IncludeExpectedOutput(string expectedOutput) + { + // PROTOTYPE: Enable. +#if RuntimeSupport + return expectedOutput; +#else + return null; +#endif + } + + [CombinatorialData] + [Theory] + public void RefField(bool useCompilationReference) + { + var sourceA = +@"public ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +}"; + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics( + // (3,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public ref T F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref T").WithArguments("ref fields").WithLocation(3, 12)); + + var field = comp.GetMember("S.F"); + Assert.Equal(RefKind.Ref, field.RefKind); + Assert.Equal("ref T S.F", field.ToTestDisplayString()); + + comp = CreateCompilation(sourceA); + comp.VerifyEmitDiagnostics(); + var refA = AsReference(comp, useCompilationReference); + + field = comp.GetMember("S.F"); + Assert.Equal(RefKind.Ref, field.RefKind); + Assert.Equal("ref T S.F", field.ToTestDisplayString()); + + var sourceB = +@"using System; +class Program +{ + static void Main() + { + int x = 1; + var s = new S(ref x); + s.F = 2; + Console.WriteLine(s.F); + Console.WriteLine(x); + x = 3; + Console.WriteLine(s.F); + Console.WriteLine(x); + } +}"; + // PROTOTYPE: Use of ref field should be tied to -langversion:preview, + // for field from metadata or compilation reference. + var verifier = CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"2 +2 +3 +3 +")); + comp = (CSharpCompilation)verifier.Compilation; + + field = comp.GetMember("S.F"); + Assert.Equal(RefKind.Ref, field.RefKind); + Assert.Equal("ref T S.F", field.ToTestDisplayString()); + } + + [CombinatorialData] + [Theory] + public void RefReadonlyField(bool useCompilationReference) + { + var sourceA = +@"public ref struct S +{ + public ref readonly T F; + public S(in T t) + { + F = ref t; + } +}"; + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics( + // (3,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public ref readonly T F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref readonly T").WithArguments("ref fields").WithLocation(3, 12)); + + var field = comp.GetMember("S.F"); + Assert.Equal(RefKind.RefReadOnly, field.RefKind); + Assert.Equal("ref readonly T S.F", field.ToTestDisplayString()); + + comp = CreateCompilation(sourceA); + comp.VerifyEmitDiagnostics(); + var refA = AsReference(comp, useCompilationReference); + + field = comp.GetMember("S.F"); + Assert.Equal(RefKind.RefReadOnly, field.RefKind); + Assert.Equal("ref readonly T S.F", field.ToTestDisplayString()); + + var sourceB = +@"using System; +class A +{ + internal int G; +} +class Program +{ + static void Main() + { + A a = new A(); + a.G = 1; + var s = new S(in a); + s.F.G = 2; + Console.WriteLine(s.F.G); + Console.WriteLine(a.G); + a.G = 3; + Console.WriteLine(s.F.G); + Console.WriteLine(a.G); + } +}"; + // PROTOTYPE: Use of ref field should be tied to -langversion:preview, + // for field from metadata or compilation reference. + var verifier = CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"2 +2 +3 +3 +")); + comp = (CSharpCompilation)verifier.Compilation; + + field = comp.GetMember("S.F"); + Assert.Equal(RefKind.RefReadOnly, field.RefKind); + Assert.Equal("ref readonly T S.F", field.ToTestDisplayString()); + } + + [Fact] + public void SubstitutedField() + { + var sourceA = +@".class public A +{ + .field public !0& modopt(object) modopt(int8) F +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"#pragma warning disable 169 +class B +{ + static A A; +}"; + var comp = CreateCompilation(sourceB, new[] { refA }); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp); + + var field = (SubstitutedFieldSymbol)comp.GetMember("B.A").Type.GetMember("F"); + Assert.Equal(RefKind.Ref, field.RefKind); + Assert.Equal(new[] { "System.SByte", "System.Object" }, field.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); + Assert.Equal("ref modopt(System.SByte) modopt(System.Object) System.Int32 A.F", field.ToTestDisplayString()); + } + + [Fact] + public void RetargetingField() + { + var sourceA = +@"public ref struct A +{ + public ref readonly int F; +} +"; + var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Mscorlib40); + var refA = comp.ToMetadataReference(); + + var sourceB = +@"#pragma warning disable 169 +ref struct B +{ + A A; +}"; + comp = CreateCompilation(sourceB, new[] { refA }, targetFramework: TargetFramework.Mscorlib45); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, verify: Verification.Skipped); + + var field = (RetargetingFieldSymbol)comp.GetMember("B.A").Type.GetMember("F"); + Assert.Equal(RefKind.RefReadOnly, field.RefKind); + // Currently, source symbols cannot declare RefCustomModifiers. If that + // changes, update this test to verify retargeting of RefCutomModifiers. + Assert.Equal(new string[0], field.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); + Assert.Equal("ref readonly System.Int32 A.F", field.ToTestDisplayString()); + } + + [Fact] + public void DefiniteAssignment_01() + { + var source = +@"ref struct S1 +{ + public ref T F; +} +ref struct S2 +{ + public ref T F; + public S2(ref T t) { } +} +ref struct S3 +{ + public ref T F; + public S3(ref T t) : this() { } +} +ref struct S4 +{ + public ref T F; + public S4(ref T t) + { + this = default; + } +} +class Program +{ + static void Main() + { + int i = 0; + new S1().F = ref i; + new S2().F = ref i; + new S3().F = ref i; + new S4().F = ref i; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,12): error CS0171: Field 'S2.F' must be fully assigned before control is returned to the caller + // public S2(ref T t) { } + Diagnostic(ErrorCode.ERR_UnassignedThis, "S2").WithArguments("S2.F").WithLocation(8, 12)); + } + + // PROTOTYPE: Should we report an error (or warning?) regardless of whether fields + // are auto-defaulted because it will result in a NullReferenceException (verify that's true). + [Fact] + public void DefiniteAssignment_02() + { + var source = +@"ref struct S +{ + public ref T F; + public S(T t) + { + F = t; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void Assignment_Ref() + { + var source = +@"ref struct S +{ + public ref T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + F = ref tValue; + F = ref tRef; + F = ref tOut; + F = ref tIn; + F = tValue; + F = tRef; + F = tOut; + F = tIn; + } + object P + { + init + { + F = ref GetValue(); + F = ref GetRef(); + F = ref GetRefReadonly(); + F = GetValue(); + F = GetRef(); + F = GetRefReadonly(); + } + } + static T GetValue() => throw null; + static ref T GetRef() => throw null; + static ref readonly T GetRefReadonly() => throw null; +} +class Program +{ + static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + s.F = ref tValue; + s.F = ref tRef; + s.F = ref tOut; + s.F = ref tIn; + s.F = tValue; + s.F = tRef; + s.F = tOut; + s.F = tIn; + } +}"; + // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics( + // (10,17): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // F = ref tIn; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(10, 17), + // (20,21): error CS1510: A ref or out value must be an assignable variable + // F = ref GetValue(); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetValue()").WithLocation(20, 21), + // (22,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' because it is a readonly variable + // F = ref GetRefReadonly(); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(22, 21), + // (40,20): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // s.F = ref tIn; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(40, 20)); + } + + [Fact] + public void Assignment_RefReadonly() + { + var source = +@"ref struct S +{ + public ref readonly T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + F = ref tValue; + F = ref tRef; + F = ref tOut; + F = ref tIn; + F = tValue; + F = tRef; + F = tOut; + F = tIn; + } + object P + { + init + { + F = ref GetValue(); + F = ref GetRef(); + F = ref GetRefReadonly(); + F = GetValue(); + F = GetRef(); + F = GetRefReadonly(); + } + } + static T GetValue() => throw null; + static ref T GetRef() => throw null; + static ref readonly T GetRefReadonly() => throw null; +} +class Program +{ + static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + s.F = ref tValue; + s.F = ref tRef; + s.F = ref tOut; + s.F = ref tIn; + s.F = tValue; + s.F = tRef; + s.F = tOut; + s.F = tIn; + } +}"; + // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics( + // (11,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tValue; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(11, 9), + // (12,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tRef; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(12, 9), + // (13,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tOut; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(13, 9), + // (14,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tIn; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(14, 9), + // (20,21): error CS1510: A ref or out value must be an assignable variable + // F = ref GetValue(); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetValue()").WithLocation(20, 21), + // (23,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetValue(); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(23, 13), + // (24,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetRef(); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(24, 13), + // (25,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetRefReadonly(); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(25, 13), + // (41,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // s.F = tValue; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(41, 9), + // (42,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // s.F = tRef; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(42, 9), + // (43,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // s.F = tOut; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(43, 9), + // (44,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // s.F = tIn; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 9)); + } + + [Fact] + public void Assignment_ReadonlyRef() + { + var source = +@"ref struct S +{ + public readonly ref T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + F = ref tValue; + F = ref tRef; + F = ref tOut; + F = ref tIn; + F = tValue; + F = tRef; + F = tOut; + F = tIn; + } + object P + { + init + { + F = ref GetValue(); + F = ref GetRef(); + F = ref GetRefReadonly(); + F = GetValue(); + F = GetRef(); + F = GetRefReadonly(); + } + } + static T GetValue() => throw null; + static ref T GetRef() => throw null; + static ref readonly T GetRefReadonly() => throw null; +} +class Program +{ + static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + s.F = ref tValue; + s.F = ref tRef; + s.F = ref tOut; + s.F = ref tIn; + s.F = tValue; + s.F = tRef; + s.F = tOut; + s.F = tIn; + } +}"; + // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics( + // (10,17): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // F = ref tIn; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(10, 17), + // (20,21): error CS1510: A ref or out value must be an assignable variable + // F = ref GetValue(); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetValue()").WithLocation(20, 21), + // (22,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' because it is a readonly variable + // F = ref GetRefReadonly(); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(22, 21), + // (40,20): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // s.F = ref tIn; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(40, 20), + // (41,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = tValue; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(41, 9), + // (42,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = tRef; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(42, 9), + // (43,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = tOut; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(43, 9), + // (44,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = tIn; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(44, 9)); + } + + [Fact] + public void Assignment_ReadonlyRefReadonly() + { + var source = +@"ref struct S +{ + public ref readonly T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + F = ref tValue; + F = ref tRef; + F = ref tOut; + F = ref tIn; + F = tValue; + F = tRef; + F = tOut; + F = tIn; + } + object P + { + init + { + F = ref GetValue(); + F = ref GetRef(); + F = ref GetRefReadonly(); + F = GetValue(); + F = GetRef(); + F = GetRefReadonly(); + } + } + static T GetValue() => throw null; + static ref T GetRef() => throw null; + static ref readonly T GetRefReadonly() => throw null; +} +class Program +{ + static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + s.F = ref tValue; + s.F = ref tRef; + s.F = ref tOut; + s.F = ref tIn; + s.F = tValue; + s.F = tRef; + s.F = tOut; + s.F = tIn; + } +}"; + // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics( + // (11,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tValue; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(11, 9), + // (12,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tRef; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(12, 9), + // (13,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tOut; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(13, 9), + // (14,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tIn; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(14, 9), + // (20,21): error CS1510: A ref or out value must be an assignable variable + // F = ref GetValue(); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetValue()").WithLocation(20, 21), + // (23,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetValue(); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(23, 13), + // (24,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetRef(); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(24, 13), + // (25,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetRefReadonly(); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(25, 13), + // (41,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // s.F = tValue; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(41, 9), + // (42,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // s.F = tRef; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(42, 9), + // (43,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // s.F = tOut; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(43, 9), + // (44,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // s.F = tIn; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 9)); + } + + [Fact] + public void RefReturn_Ref() + { + var source = +@"ref struct S +{ + public ref T F; + public ref T F1() => ref F; + public ref readonly T F2() => ref F; +} +class Program +{ + static ref T F1(S s) => ref s.F; + static ref T F2(ref S s) => ref s.F; + static ref T F3(out S s) { s = default; return ref s.F; } + static ref T F4(in S s) => ref s.F; + static ref readonly T F5(S s) => ref s.F; + static ref readonly T F6(ref S s) => ref s.F; + static ref readonly T F7(out S s) { s = default; return ref s.F; } + static ref readonly T F8(in S s) => ref s.F; +}"; + var comp = CreateCompilation(source); + // PROTOTYPE: Should not report ERR_RefReturnStructThis. + comp.VerifyEmitDiagnostics( + // (4,30): error CS8170: Struct members cannot return 'this' or other instance members by reference + // public ref T F1() => ref F; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(4, 30), + // (5,39): error CS8170: Struct members cannot return 'this' or other instance members by reference + // public ref readonly T F2() => ref F; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(5, 39), + // (9,39): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // static ref T F1(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(9, 39), + // (12,42): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable + // static ref T F4(in S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(12, 42), + // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // static ref readonly T F5(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); + } + + [Fact] + public void RefReturn_RefReadonly() + { + var source = +@"ref struct S +{ + public ref readonly T F; + public ref T F1() => ref F; + public ref readonly T F2() => ref F; +} +class Program +{ + static ref T F1(S s) => ref s.F; + static ref T F2(ref S s) => ref s.F; + static ref T F3(out S s) { s = default; return ref s.F; } + static ref T F4(in S s) => ref s.F; + static ref readonly T F5(S s) => ref s.F; + static ref readonly T F6(ref S s) => ref s.F; + static ref readonly T F7(out S s) { s = default; return ref s.F; } + static ref readonly T F8(in S s) => ref s.F; +}"; + var comp = CreateCompilation(source); + // PROTOTYPE: Should not report ERR_RefReturnStructThis. + comp.VerifyEmitDiagnostics( + // (4,30): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // public ref T F1() => ref F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(4, 30), + // (5,39): error CS8170: Struct members cannot return 'this' or other instance members by reference + // public ref readonly T F2() => ref F; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(5, 39), + // (9,39): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // static ref T F1(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 39), + // (10,43): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // static ref T F2(ref S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 43), + // (11,62): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // static ref T F3(out S s) { s = default; return ref s.F; } + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 62), + // (12,42): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // static ref T F4(in S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 42), + // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // static ref readonly T F5(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); + } + + + [Fact] + public void RefReturn_ReadonlyRef() + { + var source = +@"ref struct S +{ + public readonly ref T F; + public ref T F1() => ref F; + public ref readonly T F2() => ref F; +} +class Program +{ + static ref T F1(S s) => ref s.F; + static ref T F2(ref S s) => ref s.F; + static ref T F3(out S s) { s = default; return ref s.F; } + static ref T F4(in S s) => ref s.F; + static ref readonly T F5(S s) => ref s.F; + static ref readonly T F6(ref S s) => ref s.F; + static ref readonly T F7(out S s) { s = default; return ref s.F; } + static ref readonly T F8(in S s) => ref s.F; +}"; + var comp = CreateCompilation(source); + // PROTOTYPE: Should not report ERR_RefReturnStructThis. + comp.VerifyEmitDiagnostics( + // (4,30): error CS8160: A readonly field cannot be returned by writable reference + // public ref T F1() => ref F; + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "F").WithLocation(4, 30), + // (5,39): error CS8170: Struct members cannot return 'this' or other instance members by reference + // public ref readonly T F2() => ref F; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(5, 39), + // (9,39): error CS8160: A readonly field cannot be returned by writable reference + // static ref T F1(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(9, 39), + // (10,43): error CS8160: A readonly field cannot be returned by writable reference + // static ref T F2(ref S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(10, 43), + // (11,62): error CS8160: A readonly field cannot be returned by writable reference + // static ref T F3(out S s) { s = default; return ref s.F; } + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(11, 62), + // (12,42): error CS8160: A readonly field cannot be returned by writable reference + // static ref T F4(in S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(12, 42), + // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // static ref readonly T F5(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); + } + + [Fact] + public void RefReturn_ReadonlyRefReadonly() + { + var source = +@"ref struct S +{ + public readonly ref readonly T F; + public ref T F1() => ref F; + public ref readonly T F2() => ref F; +} +class Program +{ + static ref T F1(S s) => ref s.F; + static ref T F2(ref S s) => ref s.F; + static ref T F3(out S s) { s = default; return ref s.F; } + static ref T F4(in S s) => ref s.F; + static ref readonly T F5(S s) => ref s.F; + static ref readonly T F6(ref S s) => ref s.F; + static ref readonly T F7(out S s) { s = default; return ref s.F; } + static ref readonly T F8(in S s) => ref s.F; +}"; + var comp = CreateCompilation(source); + // PROTOTYPE: Should not report ERR_RefReturnStructThis. + comp.VerifyEmitDiagnostics( + // (4,30): error CS8160: A readonly field cannot be returned by writable reference + // public ref T F1() => ref F; + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "F").WithLocation(4, 30), + // (5,39): error CS8170: Struct members cannot return 'this' or other instance members by reference + // public ref readonly T F2() => ref F; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(5, 39), + // (9,39): error CS8160: A readonly field cannot be returned by writable reference + // static ref T F1(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(9, 39), + // (10,43): error CS8160: A readonly field cannot be returned by writable reference + // static ref T F2(ref S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(10, 43), + // (11,62): error CS8160: A readonly field cannot be returned by writable reference + // static ref T F3(out S s) { s = default; return ref s.F; } + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(11, 62), + // (12,42): error CS8160: A readonly field cannot be returned by writable reference + // static ref T F4(in S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(12, 42), + // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // static ref readonly T F5(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); + } + + [Fact] + public void RefParameter_Ref() + { + var source = +@"ref struct S +{ + public ref T F; + public S(ref T t) + { + F = ref t; + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + object P + { + init + { + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + } + void M() + { + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + public static void M1(T t) { } + public static void M2(ref T t) { } + public static void M3(out T t) { t = default; } + public static void M4(in T t) { } +} +class Program +{ + static void M() + { + var s = new S(); + S.M1(s.F); + S.M2(ref s.F); + S.M3(out s.F); + S.M4(s.F); + S.M4(in s.F); + } +}"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void RefParameter_RefReadonly() + { + var source = +@"ref struct S +{ + public ref readonly T F; + public S(ref T t) + { + F = ref t; + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + object P + { + init + { + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + } + void M() + { + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + public static void M1(T t) { } + public static void M2(ref T t) { } + public static void M3(out T t) { t = default; } + public static void M4(in T t) { } +} +class Program +{ + static void M() + { + var s = new S(); + S.M1(s.F); + S.M2(ref s.F); + S.M3(out s.F); + S.M4(s.F); + S.M4(in s.F); + } +}"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics( + // (8,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M2(ref F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 16), + // (9,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M3(out F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 16), + // (18,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M2(ref F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 20), + // (19,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M3(out F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(19, 20), + // (27,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M2(ref F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(27, 16), + // (28,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M3(out F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16), + // (43,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // S.M2(ref s.F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(43, 21), + // (44,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // S.M3(out s.F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 21)); + } + + [Fact] + public void RefParameter_ReadonlyRef() + { + var source = +@"ref struct S +{ + public readonly ref T F; + public S(ref T t) + { + F = ref t; + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + object P + { + init + { + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + } + void M() + { + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + public static void M1(T t) { } + public static void M2(ref T t) { } + public static void M3(out T t) { t = default; } + public static void M4(in T t) { } +} +class Program +{ + static void M() + { + var s = new S(); + S.M1(s.F); + S.M2(ref s.F); + S.M3(out s.F); + S.M4(s.F); + S.M4(in s.F); + } +}"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics( + // (27,16): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // M2(ref F); + Diagnostic(ErrorCode.ERR_RefReadonly, "F").WithLocation(27, 16), + // (28,16): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // M3(out F); + Diagnostic(ErrorCode.ERR_RefReadonly, "F").WithLocation(28, 16), + // (43,21): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // S.M2(ref s.F); + Diagnostic(ErrorCode.ERR_RefReadonly, "s.F").WithLocation(43, 21), + // (44,21): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // S.M3(out s.F); + Diagnostic(ErrorCode.ERR_RefReadonly, "s.F").WithLocation(44, 21)); + } + + [Fact] + public void RefParameter_ReadonlyRefReadonly() + { + var source = +@"ref struct S +{ + public readonly ref readonly T F; + public S(ref T t) + { + F = ref t; + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + object P + { + init + { + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + } + void M() + { + M1(F); + M2(ref F); + M3(out F); + M4(F); + M4(in F); + } + public static void M1(T t) { } + public static void M2(ref T t) { } + public static void M3(out T t) { t = default; } + public static void M4(in T t) { } +} +class Program +{ + static void M() + { + var s = new S(); + S.M1(s.F); + S.M2(ref s.F); + S.M3(out s.F); + S.M4(s.F); + S.M4(in s.F); + } +}"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics( + // (8,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M2(ref F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 16), + // (9,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M3(out F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 16), + // (18,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M2(ref F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 20), + // (19,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // M3(out F); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(19, 20), + // (27,16): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // M2(ref F); + Diagnostic(ErrorCode.ERR_RefReadonly, "F").WithLocation(27, 16), + // (28,16): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // M3(out F); + Diagnostic(ErrorCode.ERR_RefReadonly, "F").WithLocation(28, 16), + // (43,21): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // S.M2(ref s.F); + Diagnostic(ErrorCode.ERR_RefReadonly, "s.F").WithLocation(43, 21), + // (44,21): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // S.M3(out s.F); + Diagnostic(ErrorCode.ERR_RefReadonly, "s.F").WithLocation(44, 21)); + } + + [Fact] + public void AssignToReadonlyStruct() + { + var source = +@"ref struct S +{ + public ref T F1; + public ref readonly T F2; + public readonly ref T F3; + public readonly ref readonly T F4; +} +class Program +{ + static void M(in S s, T t) + { + s.F1 = t; + s.F2 = t; + s.F3 = t; + s.F4 = t; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (12,9): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable + // s.F1 = t; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "s.F1").WithArguments("variable", "in S").WithLocation(12, 9), + // (13,9): error CS8331: Cannot assign to field 'S.F2' because it is a readonly variable + // s.F2 = t; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F2").WithArguments("field", "S.F2").WithLocation(13, 9), + // (14,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F3 = t; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F3").WithLocation(14, 9), + // (15,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F4 = t; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F4").WithLocation(15, 9)); + } + + [Fact] + public void RefAssignFromReadonlyStruct() + { + var source = +@"ref struct S +{ + public ref T F1; + public ref readonly T F2; + public readonly ref T F3; + public readonly ref readonly T F4; +} +class Program +{ + static void M(in S s) + { + ref T t1 = ref s.F1; + ref T t2 = ref s.F2; + ref T t3 = ref s.F3; + ref T t4 = ref s.F4; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (12,24): error CS8330: Members of variable 'in S' cannot be used as a ref or out value because it is a readonly variable + // ref T t1 = ref s.F1; + Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "s.F1").WithArguments("variable", "in S").WithLocation(12, 24), + // (13,24): error CS8329: Cannot use field 'S.F2' as a ref or out value because it is a readonly variable + // ref T t2 = ref s.F2; + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F2").WithArguments("field", "S.F2").WithLocation(13, 24), + // (14,24): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // ref T t3 = ref s.F3; + Diagnostic(ErrorCode.ERR_RefReadonly, "s.F3").WithLocation(14, 24), + // (15,24): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // ref T t4 = ref s.F4; + Diagnostic(ErrorCode.ERR_RefReadonly, "s.F4").WithLocation(15, 24)); + } + + [Fact] + public void InitobjField() + { + var source = +@"using System; +struct S +{ + public T F; +} +ref struct R +{ + public ref S S; +} +class Program +{ + static void Main() + { + var s = new S(); + var r = new R(); + r.S = ref s; + NewField(ref r); + r.S.F = 42; + Console.WriteLine(s.F); + Console.WriteLine(r.S.F); + } + static void NewField(ref R r) + { + r.S = new S(); + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"42 +42 +")); + verifier.VerifyIL("Program.NewField", +@"{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref S R.S"" + IL_0006: initobj ""S"" + IL_000c: ret +}"); + } + + [Fact] + public void ReadWriteField() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int x = 1; + var s = new S(); + s.F = ref x; + Write(s, 42); + Console.WriteLine(Read(s)); + Console.WriteLine(x); + } + static int Read(S s) + { + return s.F; + } + static void Write(S s, int value) + { + s.F = value; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"42 +42 +")); + verifier.VerifyIL("S..ctor(ref T)", +@"{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld ""ref T S.F"" + IL_0007: ret +}"); + verifier.VerifyIL("Program.Read", +@"{ + // Code size 8 (0x8) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref int S.F"" + IL_0006: ldind.i4 + IL_0007: ret +}"); + verifier.VerifyIL("Program.Write", +@"{ + // Code size 9 (0x9) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref int S.F"" + IL_0006: ldarg.1 + IL_0007: stind.i4 + IL_0008: ret +}"); + } + + [Fact] + public void ReadWriteNestedField() + { + var source = +@"using System; +ref struct R1 +{ + public ref T F; + public R1(ref T t) { F = ref t; } +} +ref struct R2 +{ + public ref R1 R1; + public R2(ref R1 r1) { R1 = ref r1; } +} +class Program +{ + static void Main() + { + int i = 0; + var r1 = new R1(ref i); + var r2 = new R2(ref r1); + r2.R1.F = 42; + Console.WriteLine(Read(r2)); + Console.WriteLine(ReadIn(r2)); + Console.WriteLine(i); + } + static T Read(R2 r2) + { + return r2.R1.F; + } + static T ReadIn(in R2 r2) + { + return r2.R1.F; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"42 +42 +42 +")); + verifier.VerifyIL("Program.Read", +@"{ + // Code size 22 (0x16) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref R1 R2.R1"" + IL_0006: ldobj ""R1"" + IL_000b: ldfld ""ref T R1.F"" + IL_0010: ldobj ""T"" + IL_0015: ret +}"); + verifier.VerifyIL("Program.ReadIn", +@"{ + // Code size 17 (0x11) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref R1 R2.R1"" + IL_0006: ldfld ""ref T R1.F"" + IL_000b: ldobj ""T"" + IL_0010: ret +}"); + } + + [Fact] + public void ReadWriteFieldWithTemp_01() + { + var source = +@"using System; +ref struct S +{ + public ref T F; +} +class Program +{ + static void Main() + { + int i = 42; + Console.WriteLine(ReadWrite(ref i)); + } + static T ReadWrite(ref T t) + { + return new S().F = ref t; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(@"42")); + verifier.VerifyIL("Program.ReadWrite", +@"{ + // Code size 24 (0x18) + .maxstack 3 + .locals init (S V_0, + T& V_1) + IL_0000: ldloca.s V_0 + IL_0002: dup + IL_0003: initobj ""S"" + IL_0009: ldarg.0 + IL_000a: dup + IL_000b: stloc.1 + IL_000c: stfld ""ref T S.F"" + IL_0011: ldloc.1 + IL_0012: ldobj ""T"" + IL_0017: ret +}"); + } + + [Fact] + public void ReadWriteFieldWithTemp_02() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + private int _other; + public S(int other) : this() { _other = other; } +} +class Program +{ + static void Main() + { + int i = 42; + Console.WriteLine(ReadWrite(ref i)); + } + static T ReadWrite(ref T t) + { + return new S(1).F = ref t; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(@"42")); + verifier.VerifyIL("Program.ReadWrite", +@"{ + // Code size 24 (0x18) + .maxstack 3 + .locals init (S V_0, + T& V_1) + IL_0000: ldc.i4.1 + IL_0001: newobj ""S..ctor(int)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: ldarg.0 + IL_000a: dup + IL_000b: stloc.1 + IL_000c: stfld ""ref T S.F"" + IL_0011: ldloc.1 + IL_0012: ldobj ""T"" + IL_0017: ret +}"); + } + + [Fact] + public void ReadAndDiscard() + { + var source = +@"ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int i = 1; + ReadAndDiscard(ref i); + } + static void ReadAndDiscard(ref T t) + { + _ = new S(ref t).F; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("")); + verifier.VerifyIL("Program.ReadAndDiscard", +@"{ + // Code size 8 (0x8) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: newobj ""S..ctor(ref T)"" + IL_0006: pop + IL_0007: ret +}"); + } + + [Fact] + public void RefReturn_ByValueArg() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int i = 1; + var s = new S(ref i); + RefReturn(s) = 2; + Console.WriteLine(i); + } + static ref T RefReturn(S s) => ref s.F; +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (16,46): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // static ref T RefReturn(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(16, 46)); + } + + [Fact] + public void RefReturn_RefArg() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int i = 1; + var s = new S(ref i); + RefReturn(ref s) = 2; + Console.WriteLine(i); + } + static ref T RefReturn(ref S s) => ref s.F; +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); + verifier.VerifyIL("Program.RefReturn", +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ret +}"); + } + + [Fact] + public void RefReturn_InArg() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int i = 1; + var s = new S(ref i); + RefReturn(s) = 2; + Console.WriteLine(i); + } + static ref T RefReturn(in S s) => ref s.F; +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (16,49): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable + // static ref T RefReturn(in S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(16, 49)); + } + + [Fact] + public void RefReturn_OutArg() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int i = 1; + S s; + RefReturn(out s, ref i) = 2; + Console.WriteLine(i); + } + static ref T RefReturn(out S s, ref T t) + { + s = new S(ref t); + return ref s.F; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); + verifier.VerifyIL("Program.RefReturn", +@"{ + // Code size 19 (0x13) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: newobj ""S..ctor(ref T)"" + IL_0007: stobj ""S"" + IL_000c: ldarg.0 + IL_000d: ldfld ""ref T S.F"" + IL_0012: ret +}"); + } + + [Fact] + public void CompoundOperations() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int x = 42; + var s = new S(); + s.F = ref x; + Increment(s); + Console.WriteLine(s.F); + Console.WriteLine(x); + Subtract(s, 10); + Console.WriteLine(s.F); + Console.WriteLine(x); + } + static void Increment(S s) + { + s.F++; + } + static void Subtract(S s, int offset) + { + s.F -= offset; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"43 +43 +34 +34 +")); + verifier.VerifyIL("Program.Increment", +@"{ + // Code size 13 (0xd) + .maxstack 3 + IL_0000: ldarga.s V_0 + IL_0002: ldfld ""ref int S.F"" + IL_0007: dup + IL_0008: ldind.i4 + IL_0009: ldc.i4.1 + IL_000a: add + IL_000b: stind.i4 + IL_000c: ret +}"); + verifier.VerifyIL("Program.Subtract", +@"{ + // Code size 13 (0xd) + .maxstack 3 + IL_0000: ldarga.s V_0 + IL_0002: ldfld ""ref int S.F"" + IL_0007: dup + IL_0008: ldind.i4 + IL_0009: ldarg.1 + IL_000a: sub + IL_000b: stind.i4 + IL_000c: ret +}"); + } + + [Fact] + public void ConditionalOperator() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int x = 1; + int y = 2; + var sx = new S(ref x); + var sy = new S(ref y); + Console.WriteLine(ConditionalOperator(true, sx, sy)); + Console.WriteLine(ConditionalOperator(false, sx, sy)); + ConditionalOperatorRef(true, ref sx, ref sy) = 3; + ConditionalOperatorRef(false, ref sx, ref sy) = 4; + Console.WriteLine(x); + Console.WriteLine(y); + } + static T ConditionalOperator(bool b, S sx, S sy) + { + return b ? sx.F : sy.F; + } + static ref T ConditionalOperatorRef(bool b, ref S sx, ref S sy) + { + return ref b ? ref sx.F : ref sy.F; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"1 +2 +3 +4 +")); + verifier.VerifyIL("Program.ConditionalOperator", +@"{ + // Code size 27 (0x1b) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_000f + IL_0003: ldarg.2 + IL_0004: ldfld ""ref T S.F"" + IL_0009: ldobj ""T"" + IL_000e: ret + IL_000f: ldarg.1 + IL_0010: ldfld ""ref T S.F"" + IL_0015: ldobj ""T"" + IL_001a: ret +}"); + verifier.VerifyIL("Program.ConditionalOperatorRef", +@"{ + // Code size 17 (0x11) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_000a + IL_0003: ldarg.2 + IL_0004: ldfld ""ref T S.F"" + IL_0009: ret + IL_000a: ldarg.1 + IL_000b: ldfld ""ref T S.F"" + IL_0010: ret +}"); + } + + [Fact] + public void ConditionalAccess() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + object o = 1; + int i = 2; + var s1 = new S(ref o); + var s2 = new S(ref i); + Console.WriteLine(ConditionalAccess(s1)); + Console.WriteLine(ConditionalAccess(s2)); + } + static string ConditionalAccess(S s) + { + return s.F?.ToString(); + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"1 +2 +")); + verifier.VerifyIL("Program.ConditionalAccess", +@"{ + // Code size 54 (0x36) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldarga.s V_0 + IL_0002: ldfld ""ref T S.F"" + IL_0007: ldloca.s V_0 + IL_0009: initobj ""T"" + IL_000f: ldloc.0 + IL_0010: box ""T"" + IL_0015: brtrue.s IL_002a + IL_0017: ldobj ""T"" + IL_001c: stloc.0 + IL_001d: ldloca.s V_0 + IL_001f: ldloc.0 + IL_0020: box ""T"" + IL_0025: brtrue.s IL_002a + IL_0027: pop + IL_0028: ldnull + IL_0029: ret + IL_002a: constrained. ""T"" + IL_0030: callvirt ""string object.ToString()"" + IL_0035: ret +}"); + } + + [Fact] + public void InParamReorder() + { + var source = +@"using System; +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int x = 1; + int y = 2; + var sx = new S(ref x); + var sy = new S(ref y); + Reorder(sx, sy); + } + static ref S Get(ref S s) + { + return ref s; + } + static void Reorder(S sx, S sy) + { + M(y: in Get(ref sy).F, x: in Get(ref sx).F); + } + static void M(in T x, in T y) + { + Console.WriteLine(x); + Console.WriteLine(y); + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"1 +2 +")); + verifier.VerifyIL("Program.Reorder", +@"{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (T& V_0) + IL_0000: ldarga.s V_1 + IL_0002: call ""ref S Program.Get(ref S)"" + IL_0007: ldfld ""ref T S.F"" + IL_000c: stloc.0 + IL_000d: ldarga.s V_0 + IL_000f: call ""ref S Program.Get(ref S)"" + IL_0014: ldfld ""ref T S.F"" + IL_0019: ldloc.0 + IL_001a: call ""void Program.M(in T, in T)"" + IL_001f: ret +}"); + } + + [Fact] + public void RefAutoProperty() + { + var source = +@"using System; +ref struct S +{ + public ref T P { get; } + public ref readonly T Q { get; } + public S(ref T t) + { + P = t; + Q = t; + } +} +class Program +{ + static void Main() + { + int x = 1; + var s = new S(ref x); + s.P = 2; + Console.WriteLine(s.P); + Console.WriteLine(s.Q); + Console.WriteLine(x); + x = 3; + Console.WriteLine(s.P); + Console.WriteLine(s.Q); + Console.WriteLine(x); + } +}"; + var comp = CreateCompilation(source); + // PROTOTYPE: Should this scenario be supported? Test all valid combinations of { get, set, init }. + // PROTOTYPE: Why are we reporting ErrorCode.ERR_AssignReadonlyNotField for Q = t;? + comp.VerifyEmitDiagnostics( + // (4,18): error CS8145: Auto-implemented properties cannot return by reference + // public ref T P { get; } + Diagnostic(ErrorCode.ERR_AutoPropertyCannotBeRefReturning, "P").WithArguments("S.P").WithLocation(4, 18), + // (5,27): error CS8145: Auto-implemented properties cannot return by reference + // public ref readonly T Q { get; } + Diagnostic(ErrorCode.ERR_AutoPropertyCannotBeRefReturning, "Q").WithArguments("S.Q").WithLocation(5, 27), + // (9,9): error CS8331: Cannot assign to property 'S.Q' because it is a readonly variable + // Q = t; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "Q").WithArguments("property", "S.Q").WithLocation(9, 9), + // (9,9): error CS8079: Use of possibly unassigned auto-implemented property 'Q' + // Q = t; + Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "Q").WithArguments("Q").WithLocation(9, 9)); + } + + [Fact] + public void RefAccessor() + { + var source = +@"using System; +ref struct S +{ + internal T t; + internal ref T F() => ref t; +} +class Program +{ + static void Main() + { + var s = new S(); + s.t = 1; + s.F() = 2; + Console.WriteLine(s.F()); + Console.WriteLine(s.t); + s.t = 3; + Console.WriteLine(s.F()); + Console.WriteLine(s.t); + } +}"; + var comp = CreateCompilation(source); + // PROTOTYPE: Should this scenario be supported? + comp.VerifyEmitDiagnostics( + // (5,31): error CS8170: Struct members cannot return 'this' or other instance members by reference + // internal ref T F() => ref t; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "t").WithArguments("this").WithLocation(5, 31)); + } + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index 2160aa0efaca0..1f2ad5025a46f 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -7991,5 +7991,48 @@ void M(string s!!) SymbolDisplayPartKind.ParameterName, SymbolDisplayPartKind.Punctuation); } + + [Fact] + public void RefFields() + { + var source = +@"#pragma warning disable 169 +ref struct S +{ + ref T F1; + ref readonly T F2; +}"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + Verify(comp.GetMember("S.F1").ToDisplayParts(SymbolDisplayFormat.TestFormat), + "ref T S.F1", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.FieldName); + + Verify(comp.GetMember("S.F2").ToDisplayParts(SymbolDisplayFormat.TestFormat), + "ref readonly T S.F2", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.FieldName); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs new file mode 100644 index 0000000000000..71b3f8bc541a3 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs @@ -0,0 +1,208 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class RefFieldParsingTests : ParsingTests + { + public RefFieldParsingTests(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void FieldDeclaration_01(LanguageVersion languageVersion) + { + string source = "struct S { ref T F; }"; + UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion)); + + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "S"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void FieldDeclaration_02(LanguageVersion languageVersion) + { + string source = "struct S { ref readonly T F; }"; + UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion)); + + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "S"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void FieldDeclaration_03(LanguageVersion languageVersion) + { + string source = "struct S { out T F; }"; + UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,12): error CS1519: Invalid token 'out' in class, record, struct, or interface member declaration + // struct S { out T F; } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "out").WithArguments("out").WithLocation(1, 12)); + + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "S"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void FieldDeclaration_04(LanguageVersion languageVersion) + { + string source = "struct S { in T F; }"; + UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,12): error CS1519: Invalid token 'in' in class, record, struct, or interface member declaration + // struct S { in T F; } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "in").WithArguments("in").WithLocation(1, 12)); + + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "S"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + // PROTOTYPE: Should ref expressions be supported in object initializers? + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void ObjectInitializer(LanguageVersion languageVersion) + { + string source = "new S { F = ref t }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,13): error CS1525: Invalid expression term 'ref' + // new S { F = ref t } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref t").WithArguments("ref").WithLocation(1, 13)); + + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs index f6a22989d091c..857d41f4f9fab 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs @@ -134,12 +134,6 @@ static async ref readonly Task M() "; ParseAndValidate(text, TestOptions.Regular9, - // (9,27): error CS1003: Syntax error, '(' expected - // ref readonly int Field; - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("(", ";").WithLocation(9, 27), - // (9,27): error CS1026: ) expected - // ref readonly int Field; - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(9, 27), // (11,41): error CS1519: Invalid token 'operator' in class, record, struct, or interface member declaration // public static ref readonly Program operator +(Program x, Program y) Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "operator").WithArguments("operator").WithLocation(11, 41), @@ -412,6 +406,7 @@ void N() [Fact] public void InverseReadOnlyRefShouldBeIllegal() { + // PROTOTYPE: Investigate why we report additional parse errors. CreateCompilation(@" class Test { @@ -425,9 +420,15 @@ void M(readonly ref int p) // (4,12): error CS1002: ; expected // void M(readonly ref int p) Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(4, 12), - // (4,30): error CS1003: Syntax error, '(' expected + // (4,30): error CS1003: Syntax error, ',' expected // void M(readonly ref int p) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("(", ")").WithLocation(4, 30)); + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",", ")").WithLocation(4, 30), + // (5,6): error CS1002: ; expected + // { + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 6), + // (7,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(7, 1)); } [Fact] diff --git a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs index 6574621ac329c..cb01c8fd5e28c 100644 --- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs +++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs @@ -430,6 +430,10 @@ public Cci.IDefinition AsDefinition(EmitContext context) public Cci.ITypeReference GetType(EmitContext context) => _type; + public ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + + public bool IsByReference => false; + internal Cci.ITypeReference Type => _type; public Cci.IFieldDefinition GetResolvedField(EmitContext context) => this; diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs index e12d08c6ba8f5..1ce5b7b407866 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs @@ -201,6 +201,10 @@ Cci.ITypeReference Cci.IFieldReference.GetType(EmitContext context) return UnderlyingField.GetType(context); } + ImmutableArray Cci.IFieldReference.RefCustomModifiers => ImmutableArray.Empty; + + bool Cci.IFieldReference.IsByReference => false; + Cci.IFieldDefinition Cci.IFieldReference.GetResolvedField(EmitContext context) { return this; diff --git a/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs b/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs index e2508b69b4f03..063219e2034b3 100644 --- a/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs +++ b/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs @@ -41,6 +41,27 @@ internal static bool AnyRequired(this ImmutableArray + where TypeSymbol : class + { + internal readonly bool IsByRef; + internal readonly ImmutableArray> RefCustomModifiers; + internal readonly TypeSymbol Type; + internal readonly ImmutableArray> CustomModifiers; + + internal FieldInfo(bool isByRef, ImmutableArray> refCustomModifiers, TypeSymbol type, ImmutableArray> customModifiers) + { + IsByRef = isByRef; + RefCustomModifiers = refCustomModifiers; + Type = type; + CustomModifiers = customModifiers; + } + + internal FieldInfo(TypeSymbol type) : this(isByRef: false, refCustomModifiers: default, type, customModifiers: default) + { + } + } + [StructLayout(LayoutKind.Auto)] internal struct ParamInfo where TypeSymbol : class @@ -1884,7 +1905,7 @@ private static void GetSignatureCountsOrThrow(ref BlobReader signatureReader, Si parameterCount = signatureReader.ReadCompressedInteger(); } - internal TypeSymbol DecodeFieldSignature(FieldDefinitionHandle fieldHandle, out ImmutableArray> customModifiers) + internal void DecodeFieldSignature(FieldDefinitionHandle fieldHandle, out FieldInfo info) { try { @@ -1895,40 +1916,52 @@ internal TypeSymbol DecodeFieldSignature(FieldDefinitionHandle fieldHandle, out if (signatureHeader.Kind != SignatureKind.Field) { - customModifiers = default(ImmutableArray>); - return GetUnsupportedMetadataTypeSymbol(); // unsupported signature content + info = new FieldInfo(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content + } + else + { + DecodeFieldSignature(ref signatureReader, out info); } - - return DecodeFieldSignature(ref signatureReader, out customModifiers); } catch (BadImageFormatException mrEx) { - customModifiers = default(ImmutableArray>); - return GetUnsupportedMetadataTypeSymbol(mrEx); + info = new FieldInfo(GetUnsupportedMetadataTypeSymbol(mrEx)); } } // MetaImport::DecodeFieldSignature - protected TypeSymbol DecodeFieldSignature(ref BlobReader signatureReader, out ImmutableArray> customModifiers) + protected void DecodeFieldSignature(ref BlobReader signatureReader, out FieldInfo info) { - customModifiers = default; + info = default; try { SignatureTypeCode typeCode; - customModifiers = DecodeModifiersOrThrow( + bool isByRef = false; + ImmutableArray> refCustomModifiers = default; + ImmutableArray> customModifiers = DecodeModifiersOrThrow( ref signatureReader, out typeCode); - return DecodeTypeOrThrow(ref signatureReader, typeCode, out _); + if (typeCode == SignatureTypeCode.ByReference) + { + isByRef = true; + refCustomModifiers = customModifiers; + customModifiers = DecodeModifiersOrThrow( + ref signatureReader, + out typeCode); + } + + var type = DecodeTypeOrThrow(ref signatureReader, typeCode, out _); + info = new FieldInfo(isByRef, refCustomModifiers, type, customModifiers); } catch (UnsupportedSignatureContent) { - return GetUnsupportedMetadataTypeSymbol(); // unsupported signature content + info = new FieldInfo(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content } catch (BadImageFormatException mrEx) { - return GetUnsupportedMetadataTypeSymbol(mrEx); + info = new FieldInfo(GetUnsupportedMetadataTypeSymbol(mrEx)); } } diff --git a/src/Compilers/Core/Portable/PEWriter/Members.cs b/src/Compilers/Core/Portable/PEWriter/Members.cs index fcb78a61af057..94541f8198bce 100644 --- a/src/Compilers/Core/Portable/PEWriter/Members.cs +++ b/src/Compilers/Core/Portable/PEWriter/Members.cs @@ -258,6 +258,19 @@ internal interface IFieldReference : ITypeMemberReference /// ITypeReference GetType(EmitContext context); + /// + /// The list of custom modifiers, if any, associated with the ref modifier. + /// + ImmutableArray RefCustomModifiers + { + get; + } + + /// + /// True if the field is passed by reference (using a managed pointer). + /// + bool IsByReference { get; } + /// /// The Field being referred to. /// diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 17defcf93881b..ebd851f35d750 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -3337,6 +3337,12 @@ private void SerializeParameterInformation(ParameterTypeEncoder encoder, IParame private void SerializeFieldSignature(IFieldReference fieldReference, BlobBuilder builder) { var typeEncoder = new BlobEncoder(builder).FieldSignature(); + // PROTOTYPE: Add 'bool isByRef' parameter to BlobEncoder.FieldSignature(). + // PROTOTYPE: What about RefCustomModifiers? + if (fieldReference.IsByReference) + { + typeEncoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference); + } SerializeTypeReference(typeEncoder, fieldReference.GetType(Context)); } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 745c6f1fb06c3..05b12acac04f5 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,5 +1,7 @@ *REMOVED*override abstract Microsoft.CodeAnalysis.Diagnostic.Equals(object? obj) -> bool Microsoft.CodeAnalysis.Compilation.GetTypesByMetadataName(string! fullyQualifiedMetadataName) -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.IFieldSymbol.RefCustomModifiers.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.IFieldSymbol.RefKind.get -> Microsoft.CodeAnalysis.RefKind Microsoft.CodeAnalysis.IOperation.ChildOperations.get -> Microsoft.CodeAnalysis.IOperation.OperationList Microsoft.CodeAnalysis.IOperation.OperationList Microsoft.CodeAnalysis.IOperation.OperationList.Any() -> bool diff --git a/src/Compilers/Core/Portable/Symbols/IFieldSymbol.cs b/src/Compilers/Core/Portable/Symbols/IFieldSymbol.cs index 31875be2575b9..0e646cac8fdf5 100644 --- a/src/Compilers/Core/Portable/Symbols/IFieldSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IFieldSymbol.cs @@ -56,6 +56,16 @@ public interface IFieldSymbol : ISymbol /// int FixedSize { get; } + /// + /// Returns the RefKind of the field. + /// + RefKind RefKind { get; } + + /// + /// Custom modifiers associated with the ref modifier, or an empty array if there are none. + /// + ImmutableArray RefCustomModifiers { get; } + /// /// Gets the type of this field. /// diff --git a/src/Compilers/VisualBasic/Portable/Emit/FieldSymbolAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/FieldSymbolAdapter.vb index 5f4b6c1163269..f2a8618d98107 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/FieldSymbolAdapter.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/FieldSymbolAdapter.vb @@ -32,6 +32,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End If End Function + Private ReadOnly Property IFieldReferenceRefCustomModifiers As ImmutableArray(Of ICustomModifier) Implements IFieldReference.RefCustomModifiers + Get + Return ImmutableArray(Of ICustomModifier).Empty + End Get + End Property + + Private ReadOnly Property IFieldReferenceIsByReference As Boolean Implements IFieldReference.IsByReference + Get + Return False + End Get + End Property + Private Function IFieldReferenceGetResolvedField(context As EmitContext) As IFieldDefinition Implements IFieldReference.GetResolvedField Return ResolvedFieldImpl(DirectCast(context.Module, PEModuleBuilder)) End Function diff --git a/src/Compilers/VisualBasic/Portable/Emit/SpecializedFieldReference.vb b/src/Compilers/VisualBasic/Portable/Emit/SpecializedFieldReference.vb index d5cf8c6821167..8d31dd2e8972f 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/SpecializedFieldReference.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/SpecializedFieldReference.vb @@ -3,13 +3,9 @@ ' See the LICENSE file in the project root for more information. Imports System -Imports System.Collections.Generic -Imports System.Linq -Imports System.Text +Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.Emit -Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Emit @@ -65,6 +61,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit End If End Function + Private ReadOnly Property IFieldReferenceRefCustomModifiers As ImmutableArray(Of Cci.ICustomModifier) Implements Cci.IFieldReference.RefCustomModifiers + Get + Return ImmutableArray(Of Cci.ICustomModifier).Empty + End Get + End Property + + Private ReadOnly Property IFieldReferenceIsByReference As Boolean Implements Cci.IFieldReference.IsByReference + Get + Return False + End Get + End Property + Private Function IFieldReferenceGetResolvedField(context As EmitContext) As Cci.IFieldDefinition Implements Cci.IFieldReference.GetResolvedField Return Nothing End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/FieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/FieldSymbol.vb index b3d2bad9d4042..4eae77110188d 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/FieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/FieldSymbol.vb @@ -47,6 +47,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + ''' + ''' Returns the RefKind of the field. + ''' + Public ReadOnly Property RefKind As RefKind Implements IFieldSymbol.RefKind + Get + Return RefKind.None + End Get + End Property + + ''' + ''' Custom modifiers associated with the ref modifier, or an empty array if there are none. + ''' + Public ReadOnly Property RefCustomModifiers As ImmutableArray(Of CustomModifier) Implements IFieldSymbol.RefCustomModifiers + Get + Return ImmutableArray(Of CustomModifier).Empty + End Get + End Property + ''' ''' Gets the type of this variable. ''' diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb index 3e3985ba83fea..1cdc0b76f5b79 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb @@ -114,9 +114,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Return Nothing End If - Dim customModifiers As ImmutableArray(Of ModifierInfo(Of TypeSymbol)) = Nothing - Dim type As TypeSymbol = Me.DecodeFieldSignature(signaturePointer, customModifiers) - Return FindFieldBySignature(_containingType, memberName, customModifiers, type) + Dim fieldInfo As FieldInfo(Of TypeSymbol) = Nothing + Me.DecodeFieldSignature(signaturePointer, fieldInfo) + Return FindFieldBySignature(_containingType, memberName, fieldInfo.CustomModifiers, fieldInfo.Type) Case Else ' error diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb index bb09dc34f1427..fabe45e6e876e 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb @@ -347,12 +347,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Private Sub EnsureSignatureIsLoaded() If _lazyType Is Nothing Then Dim moduleSymbol = _containingType.ContainingPEModule - Dim customModifiers As ImmutableArray(Of ModifierInfo(Of TypeSymbol)) = Nothing - Dim type As TypeSymbol = New MetadataDecoder(moduleSymbol, _containingType).DecodeFieldSignature(_handle, customModifiers) + Dim fieldInfo As FieldInfo(Of TypeSymbol) = Nothing + Dim metadataDecoder = New MetadataDecoder(moduleSymbol, _containingType) + metadataDecoder.DecodeFieldSignature(_handle, fieldInfo) + Dim type As TypeSymbol = fieldInfo.Type + + ' PROTOTYPE: Report use-site diagnostic if fieldInfo.IsByRef. type = TupleTypeDecoder.DecodeTupleTypesIfApplicable(type, _handle, moduleSymbol) - ImmutableInterlocked.InterlockedCompareExchange(_lazyCustomModifiers, VisualBasicCustomModifier.Convert(customModifiers), Nothing) + ImmutableInterlocked.InterlockedCompareExchange(_lazyCustomModifiers, VisualBasicCustomModifier.Convert(fieldInfo.CustomModifiers), Nothing) Interlocked.CompareExchange(_lazyType, type, Nothing) End If End Sub diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb index 609f19d316292..b9a52db2ed9fc 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb @@ -5270,6 +5270,54 @@ End Class" SymbolDisplayPartKind.Keyword) End Sub + ''' + ''' IFieldSymbol.RefKind is ignored in VisualBasic.SymbolDisplayVisitor. + ''' + + Public Sub RefFields() + Dim source = +"#pragma warning disable 169 +ref struct S +{ + ref T F1; + ref readonly T F2; +}" + Dim comp = CreateCSharpCompilation(GetUniqueName(), source, parseOptions:=New CSharp.CSharpParseOptions(CSharp.LanguageVersion.Preview)) + comp.VerifyDiagnostics() + + Dim type = comp.GlobalNamespace.GetTypeMembers("S").Single() + + Verify(SymbolDisplay.ToDisplayParts(type.GetMembers("F1").Single(), SymbolDisplayFormat.TestFormat), + "S(Of T).F1 As T", + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Operator, + SymbolDisplayPartKind.FieldName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName) + + Verify(SymbolDisplay.ToDisplayParts(type.GetMembers("F2").Single(), SymbolDisplayFormat.TestFormat), + "S(Of T).F2 As T", + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Operator, + SymbolDisplayPartKind.FieldName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName) + End Sub + #Region "Helpers" Private Shared Sub TestSymbolDescription( diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/DisplayClassVariable.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/DisplayClassVariable.cs index d04f935e73e1a..bb86592e68f1f 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/DisplayClassVariable.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/DisplayClassVariable.cs @@ -213,6 +213,10 @@ internal override TypeWithAnnotations GetFieldType(ConsList fieldsB { return _type; } + + public override RefKind RefKind => RefKind.None; + + public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; } } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs index c67ee1d1d5839..675112828aa60 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator { internal static class SyntaxHelpers { - internal static readonly CSharpParseOptions ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionFacts.CurrentVersion); + internal static readonly CSharpParseOptions ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionFacts.CSharpNext); /// /// Parse expression. Returns null if there are any errors. diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 6962c0f4e4356..5b5fcb09adf78 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -6950,6 +6950,38 @@ .locals init (System.Range V_0) //x IL_0007: call ""System.Index System.Index.op_Implicit(int)"" IL_000c: newobj ""System.Range..ctor(System.Index, System.Index)"" IL_0011: ret +}"); + } + + [Fact] + public void RefField() + { + var source = +@"ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int i = 1; + var s = new S(ref i); + } +}"; + Evaluate(source, OutputKind.ConsoleApplication, "Program.Main", "s.F = 2").GetMethodData("<>x.<>m0").VerifyIL( +@"{ + // Code size 10 (0xa) + .maxstack 3 + .locals init (int V_0, //i + S V_1) //s + IL_0000: ldloc.1 + IL_0001: ldfld ""ref int S.F"" + IL_0006: ldc.i4.2 + IL_0007: dup + IL_0008: stind.i4 + IL_0009: ret }"); } } diff --git a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ExpansionTests.cs b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ExpansionTests.cs index e5d7237d4e135..1074711b22311 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ExpansionTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/ExpansionTests.cs @@ -2555,5 +2555,34 @@ public void ErrorValueWithNoType() Verify(FormatResult(rootExpr, value), EvalResult(rootExpr, message, "", rootExpr)); } + + [Fact(Skip = "Missing test support for ref fields")] // PROTOTYPE: Enable test. + public void RefField() + { + var source = +@"struct S +{ + public S(ref string s) + { + F = ref s; + } + public ref string F; +}"; + var runtime = new DkmClrRuntimeInstance(ReflectionUtilities.GetMscorlib(GetAssembly(source))); + using (runtime.Load()) + { + var type = runtime.GetType("S"); + var value = type.Instantiate("Hello, world!"); + var evalResult = FormatResult("s", value); + var children = GetChildren(evalResult); + Verify(children, + EvalResult( + "F", + "Hello, world!", + "string", + "s.F", + DkmEvaluationResultFlags.Expandable | DkmEvaluationResultFlags.CanFavorite)); + } + } } } diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedFieldSymbol.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedFieldSymbol.cs index 657d380893451..2c748235c1236 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedFieldSymbol.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedFieldSymbol.cs @@ -29,6 +29,10 @@ public WrappedFieldSymbol(IFieldSymbol fieldSymbol, IDocumentationCommentFormatt public object ConstantValue => _symbol.ConstantValue; + public RefKind RefKind => _symbol.RefKind; + + public ImmutableArray RefCustomModifiers => _symbol.RefCustomModifiers; + public ImmutableArray CustomModifiers => _symbol.CustomModifiers; public bool HasConstantValue => _symbol.HasConstantValue; diff --git a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationFieldSymbol.cs b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationFieldSymbol.cs index ceb695c493def..74a18d6438f73 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationFieldSymbol.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationFieldSymbol.cs @@ -79,6 +79,10 @@ public bool IsReadOnly public int FixedSize => 0; + public RefKind RefKind => RefKind.None; + + public ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + public ImmutableArray CustomModifiers { get From cf131c6d42c9e39d15d13ddeb235f8c1ac83228f Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Tue, 5 Apr 2022 16:26:14 -0700 Subject: [PATCH 02/29] Update tests --- .../Test/Semantic/Semantics/RefFieldTests.cs | 25 ++++++----- .../Test/Syntax/Parsing/RefReadonlyTests.cs | 1 - .../Test/Semantic/Semantics/RefFieldTests.vb | 41 +++++++++++++++++++ 3 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 175be0159d53e..084c6a3caae45 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -251,8 +251,8 @@ static void Main() Diagnostic(ErrorCode.ERR_UnassignedThis, "S2").WithArguments("S2.F").WithLocation(8, 12)); } - // PROTOTYPE: Should we report an error (or warning?) regardless of whether fields - // are auto-defaulted because it will result in a NullReferenceException (verify that's true). + // PROTOTYPE: Report a diagnostic when assigning a value rather than a ref + // in the constructor because a NullReferenceException will be thrown at runtime? [Fact] public void DefiniteAssignment_02() { @@ -1818,8 +1818,8 @@ ref struct S public ref readonly T Q { get; } public S(ref T t) { - P = t; - Q = t; + P = ref t; + Q = ref t; } } class Program @@ -1840,7 +1840,6 @@ static void Main() }"; var comp = CreateCompilation(source); // PROTOTYPE: Should this scenario be supported? Test all valid combinations of { get, set, init }. - // PROTOTYPE: Why are we reporting ErrorCode.ERR_AssignReadonlyNotField for Q = t;? comp.VerifyEmitDiagnostics( // (4,18): error CS8145: Auto-implemented properties cannot return by reference // public ref T P { get; } @@ -1848,11 +1847,17 @@ static void Main() // (5,27): error CS8145: Auto-implemented properties cannot return by reference // public ref readonly T Q { get; } Diagnostic(ErrorCode.ERR_AutoPropertyCannotBeRefReturning, "Q").WithArguments("S.Q").WithLocation(5, 27), - // (9,9): error CS8331: Cannot assign to property 'S.Q' because it is a readonly variable - // Q = t; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "Q").WithArguments("property", "S.Q").WithLocation(9, 9), + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // P = ref t; + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "P").WithLocation(8, 9), + // (8,9): error CS8079: Use of possibly unassigned auto-implemented property 'P' + // P = ref t; + Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "P").WithArguments("P").WithLocation(8, 9), + // (9,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // Q = ref t; + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "Q").WithLocation(9, 9), // (9,9): error CS8079: Use of possibly unassigned auto-implemented property 'Q' - // Q = t; + // Q = ref t; Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "Q").WithArguments("Q").WithLocation(9, 9)); } @@ -1881,7 +1886,7 @@ static void Main() } }"; var comp = CreateCompilation(source); - // PROTOTYPE: Should this scenario be supported? + // PROTOTYPE: Should not report ERR_RefReturnStructThis. comp.VerifyEmitDiagnostics( // (5,31): error CS8170: Struct members cannot return 'this' or other instance members by reference // internal ref T F() => ref t; diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs index 857d41f4f9fab..c5cfeeece1247 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs @@ -406,7 +406,6 @@ void N() [Fact] public void InverseReadOnlyRefShouldBeIllegal() { - // PROTOTYPE: Investigate why we report additional parse errors. CreateCompilation(@" class Test { diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb new file mode 100644 index 0000000000000..3a2d720e9a4ef --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb @@ -0,0 +1,41 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests + + Public Class RefFieldTests + Inherits BasicTestBase + + + Public Sub RefField() + Dim sourceA = +"public ref struct S +{ + private ref T F; + public S(ref T t) { F = ref t; } +}" + Dim compA = CreateCSharpCompilation(GetUniqueName(), sourceA, parseOptions:=New CSharp.CSharpParseOptions(languageVersion:=CSharp.LanguageVersion.Preview)) + compA.VerifyDiagnostics() + Dim refA = compA.EmitToImageReference() + + Dim sourceB = +"Class Program + Public Sub Main() + Dim s = New S(Of Integer)() + End Sub +End Class" + + Dim compB = CreateCompilation(sourceB, references:={refA}) + compB.AssertTheseDiagnostics( +BC30668: 'S(Of Integer)' is obsolete: 'Types with embedded references are not supported in this version of your compiler.'. + Dim s = New S(Of Integer)() + ~~~~~~~~~~~~~ +) + End Sub + + End Class + +End Namespace From 92b6a1f87c4ed50d53afa854f997bb965928ba35 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 6 Apr 2022 11:14:25 -0700 Subject: [PATCH 03/29] Update tests --- .../Test/Semantic/Semantics/RefFieldTests.cs | 168 +++++++++++++++--- 1 file changed, 147 insertions(+), 21 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 084c6a3caae45..1fa6b25abb1be 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -25,6 +25,126 @@ private static string IncludeExpectedOutput(string expectedOutput) #endif } + [CombinatorialData] + [Theory] + public void LanguageVersion(bool useCompilationReference) + { + var sourceA = +@"public ref struct S +{ + public ref T F1; + public ref readonly T F2; + public S(ref T t) + { + F1 = ref t; + F2 = ref t; + } + S(object unused, T t0) + { + this = default; + this = new S(); + this = new S(ref t0); + this = new S { F1 = t0 }; + this = default; + S s; + s = new S(); + s = new S(ref t0); + s = new S { F1 = t0 }; + } + static void M1(T t1) + { + S s1; + s1 = default; + s1 = new S(); + s1 = new S(ref t1); + s1 = new S { F1 = t1 }; + } + static void M2(ref T t2) + { + S s2; + s2 = new S(ref t2); + s2 = new S { F1 = t2 }; + } + static void M3(S s3) + { + var other = s3; + M1(s3.F1); + M1(s3.F2); + M2(ref s3.F1); + } + void M4(T t4) + { + this = default; + this = new S(); + this = new S(ref t4); + this = new S { F1 = t4 }; + S s; + s = new S(); + s = new S(ref t4); + s = new S { F1 = t4 }; + } + void M5(S s5) + { + var other = this; + M1(F1); + M1(F2); + M2(ref F1); + M1(this.F1); + M1(this.F2); + M2(ref this.F1); + M1(s5.F1); + M1(s5.F2); + M2(ref s5.F1); + } +}"; + var comp = CreateCompilation(sourceA, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics( + // (3,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public ref T F1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref T").WithArguments("ref fields").WithLocation(3, 12), + // (4,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public ref readonly T F2; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref readonly T").WithArguments("ref fields").WithLocation(4, 12)); + + comp = CreateCompilation(sourceA); + comp.VerifyEmitDiagnostics(); + var refA = AsReference(comp, useCompilationReference); + + var sourceB = +@"class Program +{ + static void M1(T t) + { + S s1; + s1 = default; + s1 = new S(); + s1 = new S(ref t); + s1 = new S { F1 = t }; + } + static void M2(ref T t) + { + S s2; + s2 = new S(ref t); + s2 = new S { F1 = t }; + } + static void M3(S s) + { + var s3 = s; + M1(s.F1); + M1(s.F2); + M2(ref s.F1); + } +}"; + + // PROTOTYPE: Use of ref field should be tied to -langversion:preview, + // for field from metadata or compilation reference. + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + } + [CombinatorialData] [Theory] public void RefField(bool useCompilationReference) @@ -41,17 +161,12 @@ public void RefField(bool useCompilationReference) // public ref T F; Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref T").WithArguments("ref fields").WithLocation(3, 12)); - var field = comp.GetMember("S.F"); - Assert.Equal(RefKind.Ref, field.RefKind); - Assert.Equal("ref T S.F", field.ToTestDisplayString()); + verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); comp = CreateCompilation(sourceA); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); - - field = comp.GetMember("S.F"); - Assert.Equal(RefKind.Ref, field.RefKind); - Assert.Equal("ref T S.F", field.ToTestDisplayString()); + verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); var sourceB = @"using System; @@ -69,8 +184,13 @@ static void Main() Console.WriteLine(x); } }"; + // PROTOTYPE: Use of ref field should be tied to -langversion:preview, // for field from metadata or compilation reference. + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics(); + verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); + var verifier = CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 @@ -78,10 +198,13 @@ static void Main() 3 ")); comp = (CSharpCompilation)verifier.Compilation; + verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); - field = comp.GetMember("S.F"); - Assert.Equal(RefKind.Ref, field.RefKind); - Assert.Equal("ref T S.F", field.ToTestDisplayString()); + static void verifyField(FieldSymbol field, RefKind refKind, string displayName) + { + Assert.Equal(refKind, field.RefKind); + Assert.Equal(displayName, field.ToTestDisplayString()); + } } [CombinatorialData] @@ -103,17 +226,12 @@ public S(in T t) // public ref readonly T F; Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref readonly T").WithArguments("ref fields").WithLocation(3, 12)); - var field = comp.GetMember("S.F"); - Assert.Equal(RefKind.RefReadOnly, field.RefKind); - Assert.Equal("ref readonly T S.F", field.ToTestDisplayString()); + verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); comp = CreateCompilation(sourceA); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); - - field = comp.GetMember("S.F"); - Assert.Equal(RefKind.RefReadOnly, field.RefKind); - Assert.Equal("ref readonly T S.F", field.ToTestDisplayString()); + verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); var sourceB = @"using System; @@ -136,8 +254,13 @@ static void Main() Console.WriteLine(a.G); } }"; + // PROTOTYPE: Use of ref field should be tied to -langversion:preview, // for field from metadata or compilation reference. + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); + comp.VerifyEmitDiagnostics(); + verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); + var verifier = CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 @@ -145,10 +268,13 @@ static void Main() 3 ")); comp = (CSharpCompilation)verifier.Compilation; + verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); - field = comp.GetMember("S.F"); - Assert.Equal(RefKind.RefReadOnly, field.RefKind); - Assert.Equal("ref readonly T S.F", field.ToTestDisplayString()); + static void verifyField(FieldSymbol field, RefKind refKind, string displayName) + { + Assert.Equal(refKind, field.RefKind); + Assert.Equal(displayName, field.ToTestDisplayString()); + } } [Fact] @@ -914,7 +1040,7 @@ static void M() // (44,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable // S.M3(out s.F); Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 21)); - } + } [Fact] public void RefParameter_ReadonlyRef() From 3e1744bbb53d88a013e24feab0ffc2fe5ae6ddd7 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 6 Apr 2022 16:47:45 -0700 Subject: [PATCH 04/29] Report -langver error at use-site --- .../Portable/Binder/Binder_Expressions.cs | 7 ++ .../Test/Semantic/Semantics/RefFieldTests.cs | 88 ++++++++++++++++--- 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 9a3e5e697a4fe..7f1eb207dd0b3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7076,6 +7076,13 @@ protected BoundExpression BindFieldAccess( CheckReceiverAndRuntimeSupportForSymbolAccess(node, receiver, fieldSymbol, diagnostics); } + if (fieldSymbol.RefKind != RefKind.None && + receiver is { } && + receiver.Kind != BoundKind.ThisReference) + { + CheckFeatureAvailability(node, MessageID.IDS_FeatureRefFields, diagnostics); + } + TypeSymbol fieldType = fieldSymbol.GetFieldType(this.FieldsBeingBound).Type; BoundExpression expr = new BoundFieldAccess(node, receiver, fieldSymbol, constantValueOpt, resultKind, fieldType, hasErrors: (hasErrors || hasError)); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 1fa6b25abb1be..a04adcfabaa16 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -104,7 +104,43 @@ void M5(S s5) Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref T").WithArguments("ref fields").WithLocation(3, 12), // (4,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public ref readonly T F2; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref readonly T").WithArguments("ref fields").WithLocation(4, 12)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref readonly T").WithArguments("ref fields").WithLocation(4, 12), + // (15,27): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // this = new S { F1 = t0 }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(15, 27), + // (20,24): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // s = new S { F1 = t0 }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(20, 24), + // (28,25): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // s1 = new S { F1 = t1 }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(28, 25), + // (34,25): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // s2 = new S { F1 = t2 }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(34, 25), + // (39,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M1(s3.F1); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s3.F1").WithArguments("ref fields").WithLocation(39, 12), + // (40,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M1(s3.F2); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s3.F2").WithArguments("ref fields").WithLocation(40, 12), + // (41,16): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M2(ref s3.F1); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s3.F1").WithArguments("ref fields").WithLocation(41, 16), + // (48,27): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // this = new S { F1 = t4 }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(48, 27), + // (52,24): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // s = new S { F1 = t4 }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(52, 24), + // (63,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M1(s5.F1); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s5.F1").WithArguments("ref fields").WithLocation(63, 12), + // (64,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M1(s5.F2); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s5.F2").WithArguments("ref fields").WithLocation(64, 12), + // (65,16): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M2(ref s5.F1); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s5.F1").WithArguments("ref fields").WithLocation(65, 16)); comp = CreateCompilation(sourceA); comp.VerifyEmitDiagnostics(); @@ -136,13 +172,29 @@ static void M3(S s) } }"; - // PROTOTYPE: Use of ref field should be tied to -langversion:preview, - // for field from metadata or compilation reference. comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); - comp.VerifyEmitDiagnostics(); + comp.VerifyEmitDiagnostics( + // (9,25): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // s1 = new S { F1 = t }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(9, 25), + // (15,25): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // s2 = new S { F1 = t }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(15, 25), + // (20,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M1(s.F1); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F1").WithArguments("ref fields").WithLocation(20, 12), + // (21,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M1(s.F2); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F2").WithArguments("ref fields").WithLocation(21, 12), + // (22,16): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M2(ref s.F1); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F1").WithArguments("ref fields").WithLocation(22, 16)); comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext); comp.VerifyEmitDiagnostics(); + + // PROTOTYPE: Verify there are no use-site diagnostics associated with the PEFieldSymbol, regardless of LanguageVersion. + // PROTOTYPE: Verify use of ref auto-property does not generate a LanguageVersion error (since we generally don't look at how the property is implemented). } [CombinatorialData] @@ -185,10 +237,18 @@ static void Main() } }"; - // PROTOTYPE: Use of ref field should be tied to -langversion:preview, - // for field from metadata or compilation reference. comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); - comp.VerifyEmitDiagnostics(); + comp.VerifyEmitDiagnostics( + // (8,9): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // s.F = 2; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F").WithArguments("ref fields").WithLocation(8, 9), + // (9,27): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Console.WriteLine(s.F); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F").WithArguments("ref fields").WithLocation(9, 27), + // (12,27): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Console.WriteLine(s.F); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F").WithArguments("ref fields").WithLocation(12, 27)); + verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); var verifier = CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @@ -255,10 +315,18 @@ static void Main() } }"; - // PROTOTYPE: Use of ref field should be tied to -langversion:preview, - // for field from metadata or compilation reference. comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); - comp.VerifyEmitDiagnostics(); + comp.VerifyEmitDiagnostics( + // (13,9): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // s.F.G = 2; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F").WithArguments("ref fields").WithLocation(13, 9), + // (14,27): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Console.WriteLine(s.F.G); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F").WithArguments("ref fields").WithLocation(14, 27), + // (17,27): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Console.WriteLine(s.F.G); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F").WithArguments("ref fields").WithLocation(17, 27)); + verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); var verifier = CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( From 7dd1026dc625b3e84ecc60a151ab131d64fdf89a Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 6 Apr 2022 20:52:11 -0700 Subject: [PATCH 05/29] Fix tests --- .../Portable/Binder/Binder_Expressions.cs | 9 +++-- .../Test/Semantic/Semantics/RefFieldTests.cs | 40 +------------------ 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 7f1eb207dd0b3..07940d30dfe59 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7076,9 +7076,12 @@ protected BoundExpression BindFieldAccess( CheckReceiverAndRuntimeSupportForSymbolAccess(node, receiver, fieldSymbol, diagnostics); } - if (fieldSymbol.RefKind != RefKind.None && - receiver is { } && - receiver.Kind != BoundKind.ThisReference) + // If this is a ref field from another compilation, check for support for ref fields. + // No need to check for a reference to a field declared in this compilation since + // we check at the declaration site. (Check RefKind after IsFromCompilation() to + // avoid cycles for source symbols. + if (!fieldSymbol.OriginalDefinition.IsFromCompilation(Compilation) && + fieldSymbol.RefKind != RefKind.None) { CheckFeatureAvailability(node, MessageID.IDS_FeatureRefFields, diagnostics); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index a04adcfabaa16..f419652990a8e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -104,43 +104,7 @@ void M5(S s5) Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref T").WithArguments("ref fields").WithLocation(3, 12), // (4,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public ref readonly T F2; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref readonly T").WithArguments("ref fields").WithLocation(4, 12), - // (15,27): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // this = new S { F1 = t0 }; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(15, 27), - // (20,24): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // s = new S { F1 = t0 }; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(20, 24), - // (28,25): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // s1 = new S { F1 = t1 }; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(28, 25), - // (34,25): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // s2 = new S { F1 = t2 }; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(34, 25), - // (39,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // M1(s3.F1); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "s3.F1").WithArguments("ref fields").WithLocation(39, 12), - // (40,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // M1(s3.F2); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "s3.F2").WithArguments("ref fields").WithLocation(40, 12), - // (41,16): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // M2(ref s3.F1); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "s3.F1").WithArguments("ref fields").WithLocation(41, 16), - // (48,27): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // this = new S { F1 = t4 }; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(48, 27), - // (52,24): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // s = new S { F1 = t4 }; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "F1").WithArguments("ref fields").WithLocation(52, 24), - // (63,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // M1(s5.F1); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "s5.F1").WithArguments("ref fields").WithLocation(63, 12), - // (64,12): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // M1(s5.F2); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "s5.F2").WithArguments("ref fields").WithLocation(64, 12), - // (65,16): error CS8652: The feature 'ref fields' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // M2(ref s5.F1); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "s5.F1").WithArguments("ref fields").WithLocation(65, 16)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref readonly T").WithArguments("ref fields").WithLocation(4, 12)); comp = CreateCompilation(sourceA); comp.VerifyEmitDiagnostics(); @@ -194,7 +158,7 @@ static void M3(S s) comp.VerifyEmitDiagnostics(); // PROTOTYPE: Verify there are no use-site diagnostics associated with the PEFieldSymbol, regardless of LanguageVersion. - // PROTOTYPE: Verify use of ref auto-property does not generate a LanguageVersion error (since we generally don't look at how the property is implemented). + // PROTOTYPE: Verify use of ref auto-property does not generate a LanguageVersion error (since we generally don't look at how properties are implemented). } [CombinatorialData] From a8f002f77f08ec76cc1162d7b61ff270ebb2feec Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 6 Apr 2022 21:02:40 -0700 Subject: [PATCH 06/29] Update tests --- .../Test/Semantic/Semantics/RefFieldTests.cs | 63 +++++++++++++++++-- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index f419652990a8e..6b683463583a9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -154,11 +154,21 @@ static void M3(S s) // M2(ref s.F1); Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F1").WithArguments("ref fields").WithLocation(22, 16)); + verifyField(comp.GetMember("S.F1"), RefKind.Ref, "ref T S.F1"); + verifyField(comp.GetMember("S.F2"), RefKind.RefReadOnly, "ref readonly T S.F2"); + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext); comp.VerifyEmitDiagnostics(); - // PROTOTYPE: Verify there are no use-site diagnostics associated with the PEFieldSymbol, regardless of LanguageVersion. - // PROTOTYPE: Verify use of ref auto-property does not generate a LanguageVersion error (since we generally don't look at how properties are implemented). + verifyField(comp.GetMember("S.F1"), RefKind.Ref, "ref T S.F1"); + verifyField(comp.GetMember("S.F2"), RefKind.RefReadOnly, "ref readonly T S.F2"); + + static void verifyField(FieldSymbol field, RefKind refKind, string displayName) + { + Assert.Equal(refKind, field.RefKind); + Assert.Equal(displayName, field.ToTestDisplayString()); + Assert.Null(field.GetUseSiteDiagnostic()); + } } [CombinatorialData] @@ -409,16 +419,16 @@ static void Main() Diagnostic(ErrorCode.ERR_UnassignedThis, "S2").WithArguments("S2.F").WithLocation(8, 12)); } - // PROTOTYPE: Report a diagnostic when assigning a value rather than a ref - // in the constructor because a NullReferenceException will be thrown at runtime? [Fact] public void DefiniteAssignment_02() { + // Should we report a warning when assigning a value rather than a ref in the + // constructor, because a NullReferenceException will be thrown at runtime? var source = @"ref struct S { public ref T F; - public S(T t) + public S(ref T t) { F = t; } @@ -1909,6 +1919,47 @@ .locals init (T V_0) }"); } + [Fact] + public void Deconstruct() + { + var source = +@"using System; +class Pair +{ + public readonly T First; + public readonly U Second; + public Pair(T first, U second) + { + First = first; + Second = second; + } + public void Deconstruct(out T first, out U second) + { + first = First; + second = Second; + } +} +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} +class Program +{ + static void Main() + { + int i = 0; + string s = null; + var s1 = new S(ref i); + var s2 = new S(ref s); + var pair = new Pair(1, ""Hello world""); + (s1.F, s2.F) = pair; + Console.WriteLine((i, s)); + } +}"; + CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(@"(1, Hello world)")); + } + [Fact] public void InParamReorder() { @@ -1998,6 +2049,8 @@ static void Main() }"; var comp = CreateCompilation(source); // PROTOTYPE: Should this scenario be supported? Test all valid combinations of { get, set, init }. + // PROTOTYPE: Verify use of ref auto-property does not generate a LanguageVersion error + // (since we generally don't look at how properties are implemented). comp.VerifyEmitDiagnostics( // (4,18): error CS8145: Auto-implemented properties cannot return by reference // public ref T P { get; } From d976ff1435d571abb700905fda3dc635cbe85200 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 7 Apr 2022 21:57:21 -0700 Subject: [PATCH 07/29] PR feedback --- .../Portable/Binder/Binder.ValueChecks.cs | 1 - .../Metadata/PE/MemberRefMetadataDecoder.cs | 3 +-- .../Symbols/Metadata/PE/PEFieldSymbol.cs | 3 +-- .../Symbols/Metadata/PE/PENamedTypeSymbol.cs | 3 +-- .../Portable/MetadataReader/MetadataDecoder.cs | 18 ++++++++---------- .../Core/Portable/PEWriter/Members.cs | 2 +- .../Metadata/PE/MemberRefMetadataDecoder.vb | 3 +-- .../Symbols/Metadata/PE/PEFieldSymbol.vb | 4 +--- 8 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 3d533c3b4c0ec..ee504b1d2dc6f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -847,7 +847,6 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, switch (fieldSymbol.RefKind) { case RefKind.None: - Debug.Assert(fieldSymbol.RefKind == RefKind.None); Error(diagnostics, ErrorCode.ERR_RefLocalOrParamExpected, node); return false; case RefKind.Ref: diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs index b4679a94d94b9..edc352556b6cb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs @@ -138,8 +138,7 @@ internal Symbol FindMember(MemberReferenceHandle memberRef, bool methodsOnly) return null; } - FieldInfo fieldInfo; - this.DecodeFieldSignature(ref signaturePointer, out fieldInfo); + FieldInfo fieldInfo = this.DecodeFieldSignature(ref signaturePointer); return FindFieldBySignature(_containingType, memberName, fieldInfo.CustomModifiers, fieldInfo.Type); default: diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs index 27e3a2eab6d8f..9c5b4d7d74b34 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs @@ -283,8 +283,7 @@ private void EnsureSignatureIsLoaded() if (_lazyType == null) { var moduleSymbol = _containingType.ContainingPEModule; - FieldInfo fieldInfo; - (new MetadataDecoder(moduleSymbol, _containingType)).DecodeFieldSignature(_handle, out fieldInfo); + FieldInfo fieldInfo = new MetadataDecoder(moduleSymbol, _containingType).DecodeFieldSignature(_handle); TypeSymbol typeSymbol = fieldInfo.Type; ImmutableArray customModifiersArray = CSharpCustomModifier.Convert(fieldInfo.CustomModifiers); diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index dff15bddbac2a..2f8eea7c3863e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -1151,8 +1151,7 @@ private void EnsureEnumUnderlyingTypeIsLoaded(UncommonProperties uncommon) if ((fieldFlags & FieldAttributes.Static) == 0) { // Instance field used to determine underlying type. - FieldInfo fieldInfo; - decoder.DecodeFieldSignature(fieldDef, out fieldInfo); + FieldInfo fieldInfo = decoder.DecodeFieldSignature(fieldDef); TypeSymbol type = fieldInfo.Type; if (type.SpecialType.IsValidEnumUnderlyingType() && !fieldInfo.CustomModifiers.AnyRequired()) diff --git a/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs b/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs index 063219e2034b3..d88dc304b4f4f 100644 --- a/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs +++ b/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs @@ -1905,7 +1905,7 @@ private static void GetSignatureCountsOrThrow(ref BlobReader signatureReader, Si parameterCount = signatureReader.ReadCompressedInteger(); } - internal void DecodeFieldSignature(FieldDefinitionHandle fieldHandle, out FieldInfo info) + internal FieldInfo DecodeFieldSignature(FieldDefinitionHandle fieldHandle) { try { @@ -1916,24 +1916,22 @@ internal void DecodeFieldSignature(FieldDefinitionHandle fieldHandle, out FieldI if (signatureHeader.Kind != SignatureKind.Field) { - info = new FieldInfo(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content + return new FieldInfo(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content } else { - DecodeFieldSignature(ref signatureReader, out info); + return DecodeFieldSignature(ref signatureReader); } } catch (BadImageFormatException mrEx) { - info = new FieldInfo(GetUnsupportedMetadataTypeSymbol(mrEx)); + return new FieldInfo(GetUnsupportedMetadataTypeSymbol(mrEx)); } } // MetaImport::DecodeFieldSignature - protected void DecodeFieldSignature(ref BlobReader signatureReader, out FieldInfo info) + protected FieldInfo DecodeFieldSignature(ref BlobReader signatureReader) { - info = default; - try { SignatureTypeCode typeCode; @@ -1953,15 +1951,15 @@ protected void DecodeFieldSignature(ref BlobReader signatureReader, out FieldInf } var type = DecodeTypeOrThrow(ref signatureReader, typeCode, out _); - info = new FieldInfo(isByRef, refCustomModifiers, type, customModifiers); + return new FieldInfo(isByRef, refCustomModifiers, type, customModifiers); } catch (UnsupportedSignatureContent) { - info = new FieldInfo(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content + return new FieldInfo(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content } catch (BadImageFormatException mrEx) { - info = new FieldInfo(GetUnsupportedMetadataTypeSymbol(mrEx)); + return new FieldInfo(GetUnsupportedMetadataTypeSymbol(mrEx)); } } diff --git a/src/Compilers/Core/Portable/PEWriter/Members.cs b/src/Compilers/Core/Portable/PEWriter/Members.cs index 94541f8198bce..ca10b4a52cf01 100644 --- a/src/Compilers/Core/Portable/PEWriter/Members.cs +++ b/src/Compilers/Core/Portable/PEWriter/Members.cs @@ -267,7 +267,7 @@ ImmutableArray RefCustomModifiers } /// - /// True if the field is passed by reference (using a managed pointer). + /// True if the field contains a managed pointer. /// bool IsByReference { get; } diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb index 1cdc0b76f5b79..4e0a0366c03f4 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb @@ -114,8 +114,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Return Nothing End If - Dim fieldInfo As FieldInfo(Of TypeSymbol) = Nothing - Me.DecodeFieldSignature(signaturePointer, fieldInfo) + Dim fieldInfo As FieldInfo(Of TypeSymbol) = Me.DecodeFieldSignature(signaturePointer) Return FindFieldBySignature(_containingType, memberName, fieldInfo.CustomModifiers, fieldInfo.Type) Case Else diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb index fabe45e6e876e..46fcf249f7f93 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb @@ -347,9 +347,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Private Sub EnsureSignatureIsLoaded() If _lazyType Is Nothing Then Dim moduleSymbol = _containingType.ContainingPEModule - Dim fieldInfo As FieldInfo(Of TypeSymbol) = Nothing - Dim metadataDecoder = New MetadataDecoder(moduleSymbol, _containingType) - metadataDecoder.DecodeFieldSignature(_handle, fieldInfo) + Dim fieldInfo As FieldInfo(Of TypeSymbol) = New MetadataDecoder(moduleSymbol, _containingType).DecodeFieldSignature(_handle) Dim type As TypeSymbol = fieldInfo.Type ' PROTOTYPE: Report use-site diagnostic if fieldInfo.IsByRef. From 86ce6925860bdfa8a180a24c39b2a9414f197608 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Fri, 8 Apr 2022 14:48:12 -0700 Subject: [PATCH 08/29] PR feedback --- .../Portable/Binder/Binder.ValueChecks.cs | 29 +- .../Test/Semantic/Semantics/RefFieldTests.cs | 370 +++++++++++------- 2 files changed, 254 insertions(+), 145 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index ee504b1d2dc6f..a256e85ba76e0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -787,7 +787,7 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, var fieldSymbol = fieldAccess.FieldSymbol; var fieldIsStatic = fieldSymbol.IsStatic; - if (RequiresAssignableVariable(valueKind)) + if (fieldSymbol.IsReadOnly) { // A field is writeable unless // (1) it is readonly and we are not in a constructor or field initializer @@ -796,10 +796,12 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, // S has a mutable field x, then c.f.x is not a variable because c.f is not // writable. - if (fieldSymbol.IsReadOnly) + if (RequiresAssignableVariable(valueKind) && fieldSymbol.RefKind == RefKind.None || + RequiresRefAssignableVariable(valueKind)) { var canModifyReadonly = false; + // PROTOTYPE: Test all of these cases with ref fields (ref, ref readonly, readonly ref, readonly ref readonly) and value kinds (Ref, RefReadOnly, Out). Symbol containing = this.ContainingMemberOrLambda; if ((object)containing != null && fieldIsStatic == containing.IsStatic && @@ -828,11 +830,21 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, return false; } } + } - if (fieldSymbol.RefKind == RefKind.RefReadOnly) + if (RequiresAssignableVariable(valueKind)) + { + switch (fieldSymbol.RefKind) { - ReportReadOnlyError(fieldSymbol, node, valueKind, checkingReceiver, diagnostics); - return false; + case RefKind.None: + break; + case RefKind.Ref: + return CheckIsValidReceiverForVariable(node, fieldAccess.ReceiverOpt, valueKind, diagnostics); + case RefKind.RefReadOnly: + ReportReadOnlyError(fieldSymbol, node, valueKind, checkingReceiver, diagnostics); + return false; + default: + throw ExceptionUtilities.UnexpectedValue(fieldSymbol.RefKind); } if (fieldSymbol.IsFixedSizeBuffer) @@ -1905,7 +1917,7 @@ private static ErrorCode GetStandardRValueRefEscapeError(uint escapeTo) private static void ReportReadOnlyFieldError(FieldSymbol field, SyntaxNode node, BindValueKind kind, bool checkingReceiver, BindingDiagnosticBag diagnostics) { Debug.Assert((object)field != null); - Debug.Assert(RequiresAssignableVariable(kind)); + Debug.Assert(RequiresAssignableVariable(kind) && field.RefKind == RefKind.None || RequiresRefAssignableVariable(kind)); Debug.Assert(field.Type != (object)null); // It's clearer to say that the address can't be taken than to say that the field can't be modified @@ -3831,6 +3843,11 @@ private static bool HasHome( return false; } + if (field.RefKind == RefKind.Ref) // PROTOTYPE: What about RefReadOnly? + { + return true; + } + // in readonly situations where ref to a copy is not allowed, consider fields as addressable if (addressKind == AddressKind.ReadOnlyStrict) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 6b683463583a9..cef03a991a7a4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -371,7 +371,7 @@ ref struct B Assert.Equal(RefKind.RefReadOnly, field.RefKind); // Currently, source symbols cannot declare RefCustomModifiers. If that // changes, update this test to verify retargeting of RefCutomModifiers. - Assert.Equal(new string[0], field.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); + Assert.Empty(field.RefCustomModifiers); Assert.Equal("ref readonly System.Int32 A.F", field.ToTestDisplayString()); } @@ -657,21 +657,18 @@ static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) // (22,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' because it is a readonly variable // F = ref GetRefReadonly(); Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(22, 21), - // (40,20): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // (37,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = ref tValue; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(37, 9), + // (38,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = ref tRef; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(38, 9), + // (39,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = ref tOut; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(39, 9), + // (40,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) // s.F = ref tIn; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(40, 20), - // (41,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = tValue; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(41, 9), - // (42,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = tRef; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(42, 9), - // (43,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = tOut; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(43, 9), - // (44,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = tIn; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(44, 9)); + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(40, 9)); } [Fact] @@ -777,17 +774,18 @@ public void RefReturn_Ref() } class Program { - static ref T F1(S s) => ref s.F; - static ref T F2(ref S s) => ref s.F; - static ref T F3(out S s) { s = default; return ref s.F; } - static ref T F4(in S s) => ref s.F; - static ref readonly T F5(S s) => ref s.F; - static ref readonly T F6(ref S s) => ref s.F; - static ref readonly T F7(out S s) { s = default; return ref s.F; } - static ref readonly T F8(in S s) => ref s.F; + static ref T F3(S s) => ref s.F; + static ref T F4(ref S s) => ref s.F; + static ref T F5(out S s) { s = default; return ref s.F; } + static ref T F6(in S s) => ref s.F; + static ref readonly T F7(S s) => ref s.F; + static ref readonly T F8(ref S s) => ref s.F; + static ref readonly T F9(out S s) { s = default; return ref s.F; } + static ref readonly T F10(in S s) => ref s.F; }"; var comp = CreateCompilation(source); // PROTOTYPE: Should not report ERR_RefReturnStructThis. + // PROTOTYPE: Should report errors for F5() and F9() since out parameters are implicitly scoped. comp.VerifyEmitDiagnostics( // (4,30): error CS8170: Struct members cannot return 'this' or other instance members by reference // public ref T F1() => ref F; @@ -796,13 +794,13 @@ class Program // public ref readonly T F2() => ref F; Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(5, 39), // (9,39): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter - // static ref T F1(S s) => ref s.F; + // static ref T F3(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(9, 39), // (12,42): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable - // static ref T F4(in S s) => ref s.F; + // static ref T F6(in S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(12, 42), // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter - // static ref readonly T F5(S s) => ref s.F; + // static ref readonly T F7(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); } @@ -818,17 +816,18 @@ public void RefReturn_RefReadonly() } class Program { - static ref T F1(S s) => ref s.F; - static ref T F2(ref S s) => ref s.F; - static ref T F3(out S s) { s = default; return ref s.F; } - static ref T F4(in S s) => ref s.F; - static ref readonly T F5(S s) => ref s.F; - static ref readonly T F6(ref S s) => ref s.F; - static ref readonly T F7(out S s) { s = default; return ref s.F; } - static ref readonly T F8(in S s) => ref s.F; + static ref T F3(S s) => ref s.F; + static ref T F4(ref S s) => ref s.F; + static ref T F5(out S s) { s = default; return ref s.F; } + static ref T F6(in S s) => ref s.F; + static ref readonly T F7(S s) => ref s.F; + static ref readonly T F8(ref S s) => ref s.F; + static ref readonly T F9(out S s) { s = default; return ref s.F; } + static ref readonly T F10(in S s) => ref s.F; }"; var comp = CreateCompilation(source); // PROTOTYPE: Should not report ERR_RefReturnStructThis. + // PROTOTYPE: Should report error for F9() since out parameters are implicitly scoped. comp.VerifyEmitDiagnostics( // (4,30): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable // public ref T F1() => ref F; @@ -837,23 +836,22 @@ class Program // public ref readonly T F2() => ref F; Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(5, 39), // (9,39): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable - // static ref T F1(S s) => ref s.F; + // static ref T F3(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 39), // (10,43): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable - // static ref T F2(ref S s) => ref s.F; + // static ref T F4(ref S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 43), // (11,62): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable - // static ref T F3(out S s) { s = default; return ref s.F; } + // static ref T F5(out S s) { s = default; return ref s.F; } Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 62), // (12,42): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable - // static ref T F4(in S s) => ref s.F; + // static ref T F6(in S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 42), // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter - // static ref readonly T F5(S s) => ref s.F; + // static ref readonly T F7(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); } - [Fact] public void RefReturn_ReadonlyRef() { @@ -866,38 +864,33 @@ public void RefReturn_ReadonlyRef() } class Program { - static ref T F1(S s) => ref s.F; - static ref T F2(ref S s) => ref s.F; - static ref T F3(out S s) { s = default; return ref s.F; } - static ref T F4(in S s) => ref s.F; - static ref readonly T F5(S s) => ref s.F; - static ref readonly T F6(ref S s) => ref s.F; - static ref readonly T F7(out S s) { s = default; return ref s.F; } - static ref readonly T F8(in S s) => ref s.F; + static ref T F3(S s) => ref s.F; + static ref T F4(ref S s) => ref s.F; + static ref T F5(out S s) { s = default; return ref s.F; } + static ref T F6(in S s) => ref s.F; + static ref readonly T F7(S s) => ref s.F; + static ref readonly T F8(ref S s) => ref s.F; + static ref readonly T F9(out S s) { s = default; return ref s.F; } + static ref readonly T F10(in S s) => ref s.F; }"; var comp = CreateCompilation(source); // PROTOTYPE: Should not report ERR_RefReturnStructThis. + // PROTOTYPE: Should report errors for F5() and F9() since out parameters are implicitly scoped. comp.VerifyEmitDiagnostics( - // (4,30): error CS8160: A readonly field cannot be returned by writable reference + // (4,30): error CS8170: Struct members cannot return 'this' or other instance members by reference // public ref T F1() => ref F; - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "F").WithLocation(4, 30), + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(4, 30), // (5,39): error CS8170: Struct members cannot return 'this' or other instance members by reference // public ref readonly T F2() => ref F; Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(5, 39), - // (9,39): error CS8160: A readonly field cannot be returned by writable reference - // static ref T F1(S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(9, 39), - // (10,43): error CS8160: A readonly field cannot be returned by writable reference - // static ref T F2(ref S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(10, 43), - // (11,62): error CS8160: A readonly field cannot be returned by writable reference - // static ref T F3(out S s) { s = default; return ref s.F; } - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(11, 62), - // (12,42): error CS8160: A readonly field cannot be returned by writable reference - // static ref T F4(in S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(12, 42), + // (9,39): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // static ref T F3(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(9, 39), + // (12,42): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable + // static ref T F6(in S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(12, 42), // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter - // static ref readonly T F5(S s) => ref s.F; + // static ref readonly T F7(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); } @@ -913,38 +906,39 @@ public void RefReturn_ReadonlyRefReadonly() } class Program { - static ref T F1(S s) => ref s.F; - static ref T F2(ref S s) => ref s.F; - static ref T F3(out S s) { s = default; return ref s.F; } - static ref T F4(in S s) => ref s.F; - static ref readonly T F5(S s) => ref s.F; - static ref readonly T F6(ref S s) => ref s.F; - static ref readonly T F7(out S s) { s = default; return ref s.F; } - static ref readonly T F8(in S s) => ref s.F; + static ref T F3(S s) => ref s.F; + static ref T F4(ref S s) => ref s.F; + static ref T F5(out S s) { s = default; return ref s.F; } + static ref T F6(in S s) => ref s.F; + static ref readonly T F7(S s) => ref s.F; + static ref readonly T F8(ref S s) => ref s.F; + static ref readonly T F9(out S s) { s = default; return ref s.F; } + static ref readonly T F10(in S s) => ref s.F; }"; var comp = CreateCompilation(source); // PROTOTYPE: Should not report ERR_RefReturnStructThis. + // PROTOTYPE: Should report error for F9() since out parameters are implicitly scoped. comp.VerifyEmitDiagnostics( - // (4,30): error CS8160: A readonly field cannot be returned by writable reference + // (4,30): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable // public ref T F1() => ref F; - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "F").WithLocation(4, 30), + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(4, 30), // (5,39): error CS8170: Struct members cannot return 'this' or other instance members by reference // public ref readonly T F2() => ref F; Diagnostic(ErrorCode.ERR_RefReturnStructThis, "F").WithArguments("this").WithLocation(5, 39), - // (9,39): error CS8160: A readonly field cannot be returned by writable reference - // static ref T F1(S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(9, 39), - // (10,43): error CS8160: A readonly field cannot be returned by writable reference - // static ref T F2(ref S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(10, 43), - // (11,62): error CS8160: A readonly field cannot be returned by writable reference - // static ref T F3(out S s) { s = default; return ref s.F; } - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(11, 62), - // (12,42): error CS8160: A readonly field cannot be returned by writable reference - // static ref T F4(in S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonly, "s.F").WithLocation(12, 42), + // (9,39): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // static ref T F3(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 39), + // (10,43): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // static ref T F4(ref S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 43), + // (11,62): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // static ref T F5(out S s) { s = default; return ref s.F; } + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 62), + // (12,42): error CS8333: Cannot return field 'S.F' by writable reference because it is a readonly variable + // static ref T F6(in S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 42), // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter - // static ref readonly T F5(S s) => ref s.F; + // static ref readonly T F7(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); } @@ -1136,20 +1130,9 @@ static void M() S.M4(in s.F); } }"; + // PROTOTYPE: Execute code and verify IL for each of the cases. var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); - comp.VerifyEmitDiagnostics( - // (27,16): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) - // M2(ref F); - Diagnostic(ErrorCode.ERR_RefReadonly, "F").WithLocation(27, 16), - // (28,16): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) - // M3(out F); - Diagnostic(ErrorCode.ERR_RefReadonly, "F").WithLocation(28, 16), - // (43,21): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) - // S.M2(ref s.F); - Diagnostic(ErrorCode.ERR_RefReadonly, "s.F").WithLocation(43, 21), - // (44,21): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) - // S.M3(out s.F); - Diagnostic(ErrorCode.ERR_RefReadonly, "s.F").WithLocation(44, 21)); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -1218,18 +1201,18 @@ static void M() // (19,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable // M3(out F); Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(19, 20), - // (27,16): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + // (27,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable // M2(ref F); - Diagnostic(ErrorCode.ERR_RefReadonly, "F").WithLocation(27, 16), - // (28,16): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(27, 16), + // (28,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable // M3(out F); - Diagnostic(ErrorCode.ERR_RefReadonly, "F").WithLocation(28, 16), - // (43,21): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16), + // (43,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable // S.M2(ref s.F); - Diagnostic(ErrorCode.ERR_RefReadonly, "s.F").WithLocation(43, 21), - // (44,21): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(43, 21), + // (44,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable // S.M3(out s.F); - Diagnostic(ErrorCode.ERR_RefReadonly, "s.F").WithLocation(44, 21)); + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 21)); } [Fact] @@ -1261,12 +1244,12 @@ static void M(in S s, T t) // (13,9): error CS8331: Cannot assign to field 'S.F2' because it is a readonly variable // s.F2 = t; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F2").WithArguments("field", "S.F2").WithLocation(13, 9), - // (14,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // (14,9): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable // s.F3 = t; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F3").WithLocation(14, 9), - // (15,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "s.F3").WithArguments("variable", "in S").WithLocation(14, 9), + // (15,9): error CS8331: Cannot assign to field 'S.F4' because it is a readonly variable // s.F4 = t; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F4").WithLocation(15, 9)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F4").WithArguments("field", "S.F4").WithLocation(15, 9)); } [Fact] @@ -1282,28 +1265,35 @@ public void RefAssignFromReadonlyStruct() } class Program { - static void M(in S s) + static void M1(in S s1) { - ref T t1 = ref s.F1; - ref T t2 = ref s.F2; - ref T t3 = ref s.F3; - ref T t4 = ref s.F4; + ref T t1 = ref s1.F1; + ref T t2 = ref s1.F2; + ref T t3 = ref s1.F3; + ref T t4 = ref s1.F4; + } + static void M2(in S s2) + { + ref readonly T t1 = ref s2.F1; + ref readonly T t2 = ref s2.F2; + ref readonly T t3 = ref s2.F3; + ref readonly T t4 = ref s2.F4; } }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (12,24): error CS8330: Members of variable 'in S' cannot be used as a ref or out value because it is a readonly variable - // ref T t1 = ref s.F1; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "s.F1").WithArguments("variable", "in S").WithLocation(12, 24), + // ref T t1 = ref s1.F1; + Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "s1.F1").WithArguments("variable", "in S").WithLocation(12, 24), // (13,24): error CS8329: Cannot use field 'S.F2' as a ref or out value because it is a readonly variable - // ref T t2 = ref s.F2; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F2").WithArguments("field", "S.F2").WithLocation(13, 24), - // (14,24): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) - // ref T t3 = ref s.F3; - Diagnostic(ErrorCode.ERR_RefReadonly, "s.F3").WithLocation(14, 24), - // (15,24): error CS0192: A readonly field cannot be used as a ref or out value (except in a constructor) - // ref T t4 = ref s.F4; - Diagnostic(ErrorCode.ERR_RefReadonly, "s.F4").WithLocation(15, 24)); + // ref T t2 = ref s1.F2; + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s1.F2").WithArguments("field", "S.F2").WithLocation(13, 24), + // (14,24): error CS8330: Members of variable 'in S' cannot be used as a ref or out value because it is a readonly variable + // ref T t3 = ref s1.F3; + Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "s1.F3").WithArguments("variable", "in S").WithLocation(14, 24), + // (15,24): error CS8329: Cannot use field 'S.F4' as a ref or out value because it is a readonly variable + // ref T t4 = ref s1.F4; + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s1.F4").WithArguments("field", "S.F4").WithLocation(15, 24)); } [Fact] @@ -1579,12 +1569,19 @@ static void Main() { int i = 1; ReadAndDiscard(ref i); + ReadAndDiscardNoArg(); } static void ReadAndDiscard(ref T t) { _ = new S(ref t).F; } + static void ReadAndDiscardNoArg() + { + _ = new S().F; + } }"; + // PROTOTYPE: The dereference of `new S(...).F` should not be elided + // since the behavior may be observable as a NullReferenceException. var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("")); verifier.VerifyIL("Program.ReadAndDiscard", @"{ @@ -1594,6 +1591,12 @@ .maxstack 1 IL_0001: newobj ""S..ctor(ref T)"" IL_0006: pop IL_0007: ret +}"); + verifier.VerifyIL("Program.ReadAndDiscardNoArg", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret }"); } @@ -1614,15 +1617,21 @@ static void Main() int i = 1; var s = new S(ref i); RefReturn(s) = 2; + i = RefReadonlyReturn(s); Console.WriteLine(i); } static ref T RefReturn(S s) => ref s.F; + static ref readonly T RefReadonlyReturn(S s) => ref s.F; }"; + // PROTOTYPE: Should compile without errors. var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (16,46): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // (17,46): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter // static ref T RefReturn(S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(16, 46)); + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(17, 46), + // (18,63): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter + // static ref readonly T RefReadonlyReturn(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(18, 63)); } [Fact] @@ -1642,19 +1651,23 @@ static void Main() int i = 1; var s = new S(ref i); RefReturn(ref s) = 2; + i = RefReadonlyReturn(ref s); Console.WriteLine(i); } static ref T RefReturn(ref S s) => ref s.F; + static ref readonly T RefReadonlyReturn(ref S s) => ref s.F; }"; var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); - verifier.VerifyIL("Program.RefReturn", + var expectedIL = @"{ // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld ""ref T S.F"" IL_0006: ret -}"); +}"; + verifier.VerifyIL("Program.RefReturn", expectedIL); + verifier.VerifyIL("Program.RefReadonlyReturn", expectedIL); } [Fact] @@ -1674,15 +1687,17 @@ static void Main() int i = 1; var s = new S(ref i); RefReturn(s) = 2; + i = RefReadonlyReturn(s); Console.WriteLine(i); } static ref T RefReturn(in S s) => ref s.F; + static ref readonly T RefReadonlyReturn(in S s) => ref s.F; }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (16,49): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable + // (17,49): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable // static ref T RefReturn(in S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(16, 49)); + Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(17, 49)); } [Fact] @@ -1702,6 +1717,7 @@ static void Main() int i = 1; S s; RefReturn(out s, ref i) = 2; + i = RefReadonlyReturn(out s, ref i); Console.WriteLine(i); } static ref T RefReturn(out S s, ref T t) @@ -1709,9 +1725,14 @@ static ref T RefReturn(out S s, ref T t) s = new S(ref t); return ref s.F; } + static ref readonly T RefReadonlyReturn(out S s, ref T t) + { + s = new S(ref t); + return ref s.F; + } }"; var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); - verifier.VerifyIL("Program.RefReturn", + var expectedIL = @"{ // Code size 19 (0x13) .maxstack 2 @@ -1722,9 +1743,13 @@ .maxstack 2 IL_000c: ldarg.0 IL_000d: ldfld ""ref T S.F"" IL_0012: ret -}"); +}"; + verifier.VerifyIL("Program.RefReturn", expectedIL); + verifier.VerifyIL("Program.RefReadonlyReturn", expectedIL); } + // PROTOTYPE: Test with { ref readonly, readonly ref, readonly ref readonly }. + // PROTOTYPE: Test from constructor and from instance method. [Fact] public void CompoundOperations() { @@ -1952,12 +1977,38 @@ static void Main() string s = null; var s1 = new S(ref i); var s2 = new S(ref s); - var pair = new Pair(1, ""Hello world""); - (s1.F, s2.F) = pair; + Deconstruct(new Pair(1, ""Hello world""), s1, s2); Console.WriteLine((i, s)); } + static void Deconstruct(Pair pair, S s1, S s2) + { + (s1.F, s2.F) = pair; + } }"; - CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(@"(1, Hello world)")); + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(@"(1, Hello world)")); + verifier.VerifyIL("Program.Deconstruct", +@"{ + // Code size 39 (0x27) + .maxstack 4 + .locals init (T& V_0, + T V_1, + U V_2) + IL_0000: ldarga.s V_1 + IL_0002: ldfld ""ref T S.F"" + IL_0007: stloc.0 + IL_0008: ldarga.s V_2 + IL_000a: ldfld ""ref U S.F"" + IL_000f: ldarg.0 + IL_0010: ldloca.s V_1 + IL_0012: ldloca.s V_2 + IL_0014: callvirt ""void Pair.Deconstruct(out T, out U)"" + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: stobj ""T"" + IL_0020: ldloc.2 + IL_0021: stobj ""U"" + IL_0026: ret +}"); } [Fact] @@ -2045,12 +2096,15 @@ static void Main() Console.WriteLine(s.P); Console.WriteLine(s.Q); Console.WriteLine(x); + s.P = ref x; + s.Q = ref x; } }"; var comp = CreateCompilation(source); // PROTOTYPE: Should this scenario be supported? Test all valid combinations of { get, set, init }. // PROTOTYPE: Verify use of ref auto-property does not generate a LanguageVersion error // (since we generally don't look at how properties are implemented). + // PROTOTYPE: Change text of ERR_RefLocalOrParamExpected to "The left-hand side of a ref assignment must be a ref variable." comp.VerifyEmitDiagnostics( // (4,18): error CS8145: Auto-implemented properties cannot return by reference // public ref T P { get; } @@ -2069,11 +2123,17 @@ static void Main() Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "Q").WithLocation(9, 9), // (9,9): error CS8079: Use of possibly unassigned auto-implemented property 'Q' // Q = ref t; - Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "Q").WithArguments("Q").WithLocation(9, 9)); + Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "Q").WithArguments("Q").WithLocation(9, 9), + // (26,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // s.P = ref x; + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.P").WithLocation(26, 9), + // (27,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // s.Q = ref x; + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.Q").WithLocation(27, 9)); } [Fact] - public void RefAccessor() + public void RefAccessor_Value() { var source = @"using System; @@ -2083,6 +2143,38 @@ ref struct S internal ref T F() => ref t; } class Program +{ + static void Main() + { + var s = new S(); + s.t = 1; + s.F() = 2; + Console.WriteLine(s.F()); + Console.WriteLine(s.t); + s.t = 3; + Console.WriteLine(s.F()); + Console.WriteLine(s.t); + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,31): error CS8170: Struct members cannot return 'this' or other instance members by reference + // internal ref T F() => ref t; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "t").WithArguments("this").WithLocation(5, 31)); + } + + [Fact] + public void RefAccessor_Ref() + { + var source = +@"using System; +ref struct S +{ + internal ref T t; + internal ref T F() => ref t; + internal S(ref T t) { this.t = ref t; } +} +class Program { static void Main() { From b577fbac7997b3da70f675187ca557fbd0e1bb04 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 13 Apr 2022 08:14:55 -0700 Subject: [PATCH 09/29] Update error message --- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../Semantic/Semantics/RefEscapingTests.cs | 20 ++++----- .../Test/Semantic/Semantics/RefFieldTests.cs | 9 ++-- .../Semantics/RefLocalsAndReturnsTests.cs | 42 +++++++++---------- 17 files changed, 62 insertions(+), 63 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index c5408fa46211a..216d96d3d1848 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5786,7 +5786,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Tuple types used as operands of an == or != operator must have matching cardinalities. But this operator has tuple types of cardinality {0} on the left and {1} on the right. - The left-hand side of a ref assignment must be a ref local or parameter. + The left-hand side of a ref assignment must be a ref variable. Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 4196abcb9fbec..1b21c5a32a23a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - Levá strana přiřazení odkazu musí být lokální proměnná nebo parametr odkazu. + The left-hand side of a ref assignment must be a ref variable. + Levá strana přiřazení odkazu musí být lokální proměnná nebo parametr odkazu. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ea3d8c9d865c5..59c3979610069 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - Die linke Seite einer ref-Zuweisung muss ein lokaler Verweis oder ein Parameter sein. + The left-hand side of a ref assignment must be a ref variable. + Die linke Seite einer ref-Zuweisung muss ein lokaler Verweis oder ein Parameter sein. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 17cac02bfb70f..4479c62bef177 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - La parte izquierda de una asignación de referencias debe ser una referencia local o un parámetro. + The left-hand side of a ref assignment must be a ref variable. + La parte izquierda de una asignación de referencias debe ser una referencia local o un parámetro. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 7dba4dfa3e488..9351b765cb6fc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - La partie gauche d'une assignation par référence doit être une variable locale ou un paramètre ref. + The left-hand side of a ref assignment must be a ref variable. + La partie gauche d'une assignation par référence doit être une variable locale ou un paramètre ref. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 52fe2b9f2b6cc..8d5d7cf504404 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - La parte sinistra di un'assegnazione ref deve essere un parametro o una variabile locale ref. + The left-hand side of a ref assignment must be a ref variable. + La parte sinistra di un'assegnazione ref deve essere un parametro o una variabile locale ref. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index f4918e2d875c3..b54afa95f1a18 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - ref 代入の左辺は、ref ローカルまたはパラメーターにする必要があります。 + The left-hand side of a ref assignment must be a ref variable. + ref 代入の左辺は、ref ローカルまたはパラメーターにする必要があります。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 42bbe0e3fe2e1..eece2866ecb3b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - 참조 할당의 왼쪽은 참조 로컬 또는 매개 변수여야 합니다. + The left-hand side of a ref assignment must be a ref variable. + 참조 할당의 왼쪽은 참조 로컬 또는 매개 변수여야 합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index b093e2e5a6ebc..6d5fdec4e74b7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - Lewa strona przypisania odwołania musi być odwołaniem lokalnym lub parametrem. + The left-hand side of a ref assignment must be a ref variable. + Lewa strona przypisania odwołania musi być odwołaniem lokalnym lub parametrem. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 4592073c49f76..a79eb13ce80ff 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - O lado esquerdo da atribuição ref precisa ser um parâmetro ou local ref. + The left-hand side of a ref assignment must be a ref variable. + O lado esquerdo da atribuição ref precisa ser um parâmetro ou local ref. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 0c0a1b4fb69ec..a07001f1e16ac 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - Левая часть выражения назначения ссылки должна быть локальной ссылкой или параметром. + The left-hand side of a ref assignment must be a ref variable. + Левая часть выражения назначения ссылки должна быть локальной ссылкой или параметром. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 4b0410d40011d..a365f7dc89c78 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - ref atamasının sol tarafı, yerel ref veya parametresi olmalıdır. + The left-hand side of a ref assignment must be a ref variable. + ref atamasının sol tarafı, yerel ref veya parametresi olmalıdır. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 2b33dbfc20ed1..ca5e357d5aac1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - ref 赋值左侧必须为 ref 本地函数或参数。 + The left-hand side of a ref assignment must be a ref variable. + ref 赋值左侧必须为 ref 本地函数或参数。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index e3dc711f68e6d..68ad75c66f657 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1148,8 +1148,8 @@ - The left-hand side of a ref assignment must be a ref local or parameter. - 參考指派的左側必須為參考本機或參數。 + The left-hand side of a ref assignment must be a ref variable. + 參考指派的左側必須為參考本機或參數。 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index b3718b18db2e6..b2c8d573fd053 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -3620,7 +3620,7 @@ public void M(ref Test obj) this = ref obj; } }").VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref this; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(6, 9), // (6,20): error CS1510: A ref or out value must be an assignable variable @@ -3629,7 +3629,7 @@ public void M(ref Test obj) // (7,19): error CS1510: A ref or out value must be an assignable variable // obj = ref this; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "this").WithArguments("this").WithLocation(7, 19), - // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref obj; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(8, 9)); } @@ -3647,13 +3647,13 @@ public void M(ref Test obj) this = ref obj; } }").VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref this; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(6, 9), // (7,9): error CS8374: Cannot ref-assign 'this' to 'obj' because 'this' has a narrower escape scope than 'obj'. // obj = ref this; Diagnostic(ErrorCode.ERR_RefAssignNarrower, "obj = ref this").WithArguments("obj", "this").WithLocation(7, 9), - // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref obj; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(8, 9)); } @@ -3671,13 +3671,13 @@ public void M(ref Test obj) this = ref obj; } }").VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref this; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(6, 9), // (7,19): error CS1510: A ref or out value must be an assignable variable // obj = ref this; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "this").WithArguments("this").WithLocation(7, 19), - // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref obj; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(8, 9)); } @@ -3695,13 +3695,13 @@ public void M(ref Test obj) this = ref obj; } }").VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref this; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(6, 9), // (7,9): error CS8374: Cannot ref-assign 'this' to 'obj' because 'this' has a narrower escape scope than 'obj'. // obj = ref this; Diagnostic(ErrorCode.ERR_RefAssignNarrower, "obj = ref this").WithArguments("obj", "this").WithLocation(7, 9), - // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref obj; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(8, 9)); } @@ -3719,13 +3719,13 @@ public void M(ref Test obj) this = ref obj; } }").VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref this; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(6, 9), // (7,19): error CS1510: A ref or out value must be an assignable variable // obj = ref this; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "this").WithArguments("this").WithLocation(7, 19), - // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref obj; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(8, 9)); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index cef03a991a7a4..fe1c23aa06c8f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -2104,7 +2104,6 @@ static void Main() // PROTOTYPE: Should this scenario be supported? Test all valid combinations of { get, set, init }. // PROTOTYPE: Verify use of ref auto-property does not generate a LanguageVersion error // (since we generally don't look at how properties are implemented). - // PROTOTYPE: Change text of ERR_RefLocalOrParamExpected to "The left-hand side of a ref assignment must be a ref variable." comp.VerifyEmitDiagnostics( // (4,18): error CS8145: Auto-implemented properties cannot return by reference // public ref T P { get; } @@ -2112,22 +2111,22 @@ static void Main() // (5,27): error CS8145: Auto-implemented properties cannot return by reference // public ref readonly T Q { get; } Diagnostic(ErrorCode.ERR_AutoPropertyCannotBeRefReturning, "Q").WithArguments("S.Q").WithLocation(5, 27), - // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // P = ref t; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "P").WithLocation(8, 9), // (8,9): error CS8079: Use of possibly unassigned auto-implemented property 'P' // P = ref t; Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "P").WithArguments("P").WithLocation(8, 9), - // (9,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (9,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // Q = ref t; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "Q").WithLocation(9, 9), // (9,9): error CS8079: Use of possibly unassigned auto-implemented property 'Q' // Q = ref t; Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "Q").WithArguments("Q").WithLocation(9, 9), - // (26,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (26,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // s.P = ref x; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.P").WithLocation(26, 9), - // (27,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (27,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // s.Q = ref x; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.Q").WithLocation(27, 9)); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 4f28508796b63..5ab04a33a4e2c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -174,7 +174,7 @@ void M(ref S s) // (6,9): error CS8374: Cannot ref-assign 'this' to 's' because 'this' has a narrower escape scope than 's'. // s = ref this; Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s = ref this").WithArguments("s", "this").WithLocation(6, 9), - // (7,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (7,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref s; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(7, 9)); } @@ -737,7 +737,7 @@ void M() // (12,22): error CS1939: Cannot pass the range variable 'c' as an out or ref parameter // rx = ref c; Diagnostic(ErrorCode.ERR_QueryOutRefRangeVariable, "c").WithArguments("c").WithLocation(12, 22), - // (13,13): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (13,13): error CS8373: The left-hand side of a ref assignment must be a ref variable. // c = ref x; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "c").WithArguments("c").WithLocation(13, 13)); } @@ -837,22 +837,22 @@ void M() } }"); comp.VerifyDiagnostics( - // (10,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (10,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref s; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(10, 9), - // (13,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (13,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref s2; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(13, 9), - // (16,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (16,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref s3; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(16, 9), - // (18,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (18,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref (new S[1])[0]; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(18, 9), - // (21,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (21,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref s4; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(21, 9), - // (24,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (24,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref s5; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(24, 9)); } @@ -869,7 +869,7 @@ void M() } }"); comp.VerifyDiagnostics( - // (6,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // this = ref (new int[1])[0]; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "this").WithLocation(6, 9)); } @@ -887,7 +887,7 @@ void M() } }"); comp.VerifyDiagnostics( - // (7,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (7,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // _f = ref (new int[1])[0]; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "_f").WithLocation(7, 9)); } @@ -905,7 +905,7 @@ void M() } }"); comp.VerifyDiagnostics( - // (7,10): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (7,10): error CS8373: The left-hand side of a ref assignment must be a ref variable. // (2 + 3) = ref x; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "2 + 3").WithLocation(7, 10)); } @@ -924,7 +924,7 @@ void M() } }"); comp.VerifyDiagnostics( - // (8,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // x = ref y; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "x").WithArguments("x").WithLocation(8, 9)); } @@ -964,7 +964,7 @@ void M() ref int M2() => ref (new int[1])[0]; }"); comp.VerifyDiagnostics( - // (8,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // M2() = ref x; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "M2()").WithLocation(8, 9)); } @@ -987,7 +987,7 @@ ref int P } }"); comp.VerifyDiagnostics( - // (8,9): error CS8355: The left-hand side of a ref assignment must be a ref local or parameter. + // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // P = ref x; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "P").WithLocation(8, 9)); } @@ -4058,7 +4058,7 @@ void M(int a, ref int b) a = ref b; } }").VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // a = ref b; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "a").WithLocation(6, 9)); } @@ -4092,7 +4092,7 @@ public void M(int[] array, ref int value) array[0] = ref value; } }").VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // array[0] = ref value; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "array[0]").WithLocation(6, 9)); @@ -4120,7 +4120,7 @@ public void M(int* ptr, ref int value) *ptr = ref value; } }", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // *ptr = ref value; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "*ptr").WithLocation(6, 9)); @@ -4148,7 +4148,7 @@ public void M(int* ptr, ref int value) ptr[0] = ref value; } }", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // ptr[0] = ref value; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "ptr[0]").WithLocation(6, 9)); @@ -4176,7 +4176,7 @@ public void M(int x) __refvalue(__makeref(x), int) = ref x; } }", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // __refvalue(__makeref(x), int) = ref x; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "__refvalue(__makeref(x), int)").WithLocation(6, 9)); @@ -4204,7 +4204,7 @@ public void M(dynamic d, ref int value) d[0] = ref value; } }", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // d[0] = ref value; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "d[0]").WithLocation(6, 9)); @@ -4232,7 +4232,7 @@ public void M(dynamic d, ref int value) d.member = ref value; } }", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref local or parameter. + // (6,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // d.member = ref value; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "d.member").WithLocation(6, 9)); From 25aca66b17bc2c5bb9dfe64da01a6b8992042793 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 18 Apr 2022 21:29:55 -0700 Subject: [PATCH 10/29] PR feedback --- .../Portable/Binder/Binder.ValueChecks.cs | 2 +- .../Portable/Binder/Binder_Expressions.cs | 4 +- .../CSharp/Portable/CodeGen/EmitAddress.cs | 6 +- .../CSharp/Portable/CodeGen/EmitExpression.cs | 4 +- .../LambdaCapturedVariable.cs | 4 + .../StateMachineFieldSymbol.cs | 6 +- .../SynthesizedEnumValueFieldSymbol.cs | 5 + .../Synthesized/SynthesizedFieldSymbol.cs | 5 + .../Synthesized/SynthesizedFieldSymbolBase.cs | 4 - .../SynthesizedLambdaCacheFieldSymbol.cs | 5 + .../Symbols/Tuples/TupleErrorFieldSymbol.cs | 4 + .../Symbols/Tuples/TupleFieldSymbol.cs | 4 +- .../Test/Semantic/Semantics/RefFieldTests.cs | 147 +++++++++++++----- .../Emit/NoPia/CommonEmbeddedField.cs | 4 +- .../Core/Portable/PEWriter/MetadataVisitor.cs | 1 + .../Core/Portable/PEWriter/MetadataWriter.cs | 6 +- 16 files changed, 157 insertions(+), 54 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index a256e85ba76e0..a2383709c76a8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -839,7 +839,7 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, case RefKind.None: break; case RefKind.Ref: - return CheckIsValidReceiverForVariable(node, fieldAccess.ReceiverOpt, valueKind, diagnostics); + return true; case RefKind.RefReadOnly: ReportReadOnlyError(fieldSymbol, node, valueKind, checkingReceiver, diagnostics); return false; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 07940d30dfe59..c2b06c79a531a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7078,9 +7078,9 @@ protected BoundExpression BindFieldAccess( // If this is a ref field from another compilation, check for support for ref fields. // No need to check for a reference to a field declared in this compilation since - // we check at the declaration site. (Check RefKind after IsFromCompilation() to + // we check at the declaration site. (Check RefKind after checking compilation to // avoid cycles for source symbols. - if (!fieldSymbol.OriginalDefinition.IsFromCompilation(Compilation) && + if ((object)Compilation.SourceModule != fieldSymbol.OriginalDefinition.ContainingModule && fieldSymbol.RefKind != RefKind.None) { CheckFeatureAvailability(node, MessageID.IDS_FeatureRefFields, diagnostics); diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs index e6c9da8ea9f2f..19e2501aba1ef 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs @@ -544,7 +544,11 @@ private LocalDefinition EmitInstanceFieldAddress(BoundFieldAccess fieldAccess, A //NOTE: we are not propagating AddressKind.Constrained here. // the reason is that while Constrained permits calls, it does not permit // taking field addresses, so we have to turn Constrained into writeable. - var tempOpt = EmitReceiverRef(fieldAccess.ReceiverOpt, addressKind == AddressKind.Constrained ? AddressKind.Writeable : addressKind); + var tempOpt = EmitReceiverRef( + fieldAccess.ReceiverOpt, + field.RefKind == RefKind.None ? + (addressKind == AddressKind.Constrained ? AddressKind.Writeable : addressKind) : + (addressKind != AddressKind.ReadOnlyStrict ? AddressKind.ReadOnly : addressKind)); _builder.EmitOpCode(field.RefKind == RefKind.None ? ILOpCode.Ldflda : ILOpCode.Ldfld); EmitSymbolToken(field, fieldAccess.Syntax); diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 39b76956cf95b..c53d052f61483 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -1180,7 +1180,9 @@ private bool FieldLoadPrefersRef(BoundExpression receiver) case BoundKind.FieldAccess: var fieldAccess = (BoundFieldAccess)receiver; - if (fieldAccess.FieldSymbol.IsStatic) + var field = fieldAccess.FieldSymbol; + + if (field.IsStatic || field.RefKind != RefKind.None) { return true; } diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/LambdaCapturedVariable.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/LambdaCapturedVariable.cs index 9cf23ae249234..6bbb8027b6639 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/LambdaCapturedVariable.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/LambdaCapturedVariable.cs @@ -114,6 +114,10 @@ private static TypeSymbol GetCapturedVariableFieldType(SynthesizedContainer fram return frame.TypeMap.SubstituteType(((object)local != null ? local.TypeWithAnnotations : ((ParameterSymbol)variable).TypeWithAnnotations).Type).Type; } + public override RefKind RefKind => RefKind.None; + + public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { return _type; diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineFieldSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineFieldSymbol.cs index 000caf3d716d3..c1215c815324c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineFieldSymbol.cs @@ -4,7 +4,7 @@ #nullable disable -using System; +using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -59,6 +59,10 @@ internal override bool SuppressDynamicAttribute get { return true; } } + public override RefKind RefKind => RefKind.None; + + public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { return _type; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEnumValueFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEnumValueFieldSymbol.cs index 0a6e4a5ca5950..1d123c6be164d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEnumValueFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEnumValueFieldSymbol.cs @@ -4,6 +4,7 @@ #nullable disable +using System.Collections.Immutable; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.CSharp.Emit; using Roslyn.Utilities; @@ -25,6 +26,10 @@ internal override bool SuppressDynamicAttribute get { return true; } } + public override RefKind RefKind => RefKind.None; + + public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { return TypeWithAnnotations.Create(((SourceNamedTypeSymbol)ContainingType).EnumUnderlyingType); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbol.cs index b1bc917b004c7..5a7c52b7e301f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbol.cs @@ -4,6 +4,7 @@ #nullable disable +using System.Collections.Immutable; using System.Diagnostics; using Roslyn.Utilities; @@ -32,6 +33,10 @@ public SynthesizedFieldSymbol( _type = TypeWithAnnotations.Create(type); } + public override RefKind RefKind => RefKind.None; + + public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + internal override bool SuppressDynamicAttribute { get { return true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs index 7355017307232..73a6dacbcf7ce 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbolBase.cs @@ -86,10 +86,6 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r internal abstract override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound); - public sealed override RefKind RefKind => RefKind.None; - - public sealed override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; - public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLambdaCacheFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLambdaCacheFieldSymbol.cs index 772dd85298684..b13ddba55a7a8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLambdaCacheFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLambdaCacheFieldSymbol.cs @@ -4,6 +4,7 @@ #nullable disable +using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; @@ -32,6 +33,10 @@ public SynthesizedLambdaCacheFieldSymbol(NamedTypeSymbol containingType, TypeSym // since a field update is a no-op. bool ISynthesizedMethodBodyImplementationSymbol.HasMethodBodyDependency => false; + public override RefKind RefKind => RefKind.None; + + public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { return _type; diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleErrorFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleErrorFieldSymbol.cs index fba5ce6533c4e..a2d3faed7e08a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleErrorFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleErrorFieldSymbol.cs @@ -154,6 +154,10 @@ internal override bool SuppressDynamicAttribute } } + public override RefKind RefKind => RefKind.None; + + public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { return _type; diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs index 2ed15d863ba7c..90a9d62102f4b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs @@ -121,9 +121,9 @@ public sealed override Symbol ContainingSymbol } } - public sealed override RefKind RefKind => RefKind.None; + public sealed override RefKind RefKind => _underlyingField.RefKind; - public sealed override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + public sealed override ImmutableArray RefCustomModifiers => _underlyingField.RefCustomModifiers; internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index fe1c23aa06c8f..ce2d68630544a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -4,7 +4,9 @@ #nullable disable +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -340,9 +342,7 @@ class B CompileAndVerify(comp); var field = (SubstitutedFieldSymbol)comp.GetMember("B.A").Type.GetMember("F"); - Assert.Equal(RefKind.Ref, field.RefKind); - Assert.Equal(new[] { "System.SByte", "System.Object" }, field.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); - Assert.Equal("ref modopt(System.SByte) modopt(System.Object) System.Int32 A.F", field.ToTestDisplayString()); + VerifyFieldSymbol(field, "ref modopt(System.SByte) modopt(System.Object) System.Int32 A.F", RefKind.Ref, new[] { "System.SByte", "System.Object" }); } [Fact] @@ -368,11 +368,81 @@ ref struct B CompileAndVerify(comp, verify: Verification.Skipped); var field = (RetargetingFieldSymbol)comp.GetMember("B.A").Type.GetMember("F"); - Assert.Equal(RefKind.RefReadOnly, field.RefKind); // Currently, source symbols cannot declare RefCustomModifiers. If that // changes, update this test to verify retargeting of RefCutomModifiers. - Assert.Empty(field.RefCustomModifiers); - Assert.Equal("ref readonly System.Int32 A.F", field.ToTestDisplayString()); + VerifyFieldSymbol(field, "ref readonly System.Int32 A.F", RefKind.RefReadOnly, new string[0]); + } + + [Fact] + public void TupleField() + { + var sourceA = +@".class public sealed System.ValueTuple`2 extends [mscorlib]System.ValueType +{ + .field public !T1& Item1 + .field public !T2& modopt(int8) modopt(object) Item2 +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class B +{ + static (int, object) F() => default; +}"; + var comp = CreateCompilation(sourceB, targetFramework: TargetFramework.Mscorlib40, references: new[] { refA }); + comp.VerifyEmitDiagnostics(); + + var tupleType = (NamedTypeSymbol)comp.GetMember("B.F").ReturnType; + VerifyFieldSymbol(tupleType.GetField("Item1"), "ref System.Int32 (System.Int32, System.Object).Item1", RefKind.Ref, new string[0] { }); + VerifyFieldSymbol(tupleType.GetField("Item2"), "ref modopt(System.Object) modopt(System.SByte) System.Object (System.Int32, System.Object).Item2", RefKind.Ref, new[] { "System.Object", "System.SByte" }); + } + + [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NoPiaNeedsDesktop)] + public void EmbeddedField() + { + var sourceA = +@".assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } +.assembly A +{ + .custom instance void [mscorlib]System.Runtime.InteropServices.ImportedFromTypeLibAttribute::.ctor(string) = {string('_.dll')} + .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = {string('EE8A431D-7D0C-4E13-80C7-52B9C93B9B76')} +} +.class public sealed S extends [mscorlib]System.ValueType +{ + .field public int32& modopt(object) modopt(int32) F +}"; + var refA = CompileIL(sourceA, prependDefaultHeader: false, embedInteropTypes: true); + + var sourceB = +@"class Program +{ + static void Main() + { + F(new S()); + } + static void F(object o) + { + } +}"; + var comp = CreateCompilation(sourceB, references: new[] { refA, CSharpRef }); + var refB = comp.EmitToImageReference(); + + comp = CreateCompilation("", new[] { refB }); + var module = (PEModuleSymbol)comp.GetReferencedAssemblySymbol(refB).Modules[0]; + // Read from metadata directly to inspect the embedded type. + var decoder = new MetadataDecoder(module); + var reader = module.Module.MetadataReader; + var fieldHandle = reader.FieldDefinitions.Single(handle => reader.GetString(reader.GetFieldDefinition(handle).Name) == "F"); + var fieldInfo = decoder.DecodeFieldSignature(fieldHandle); + Assert.True(fieldInfo.IsByRef); + Assert.Equal(new[] { "System.Int32", "System.Object" }, fieldInfo.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); + } + + private static void VerifyFieldSymbol(FieldSymbol field, string expectedDisplayString, RefKind expectedRefKind, string[] expectedRefCustomModifiers) + { + Assert.Equal(expectedRefKind, field.RefKind); + Assert.Equal(expectedRefCustomModifiers, field.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); + Assert.Equal(expectedDisplayString, field.ToTestDisplayString()); } [Fact] @@ -677,7 +747,7 @@ public void Assignment_ReadonlyRefReadonly() var source = @"ref struct S { - public ref readonly T F; + public readonly ref readonly T F; public S(T tValue, ref T tRef, out T tOut, in T tIn) { tOut = default; @@ -748,6 +818,18 @@ static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) // (25,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable // F = GetRefReadonly(); Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(25, 13), + // (37,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = ref tValue; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(37, 9), + // (38,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = ref tRef; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(38, 9), + // (39,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = ref tOut; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(39, 9), + // (40,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // s.F = ref tIn; + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(40, 9), // (41,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable // s.F = tValue; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(41, 9), @@ -796,9 +878,6 @@ class Program // (9,39): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter // static ref T F3(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(9, 39), - // (12,42): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable - // static ref T F6(in S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(12, 42), // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter // static ref readonly T F7(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); @@ -886,9 +965,6 @@ class Program // (9,39): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter // static ref T F3(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(9, 39), - // (12,42): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable - // static ref T F6(in S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(12, 42), // (13,48): error CS8167: Cannot return by reference a member of parameter 's' because it is not a ref or out parameter // static ref readonly T F7(S s) => ref s.F; Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); @@ -1238,15 +1314,9 @@ static void M(in S s, T t) }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (12,9): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable - // s.F1 = t; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "s.F1").WithArguments("variable", "in S").WithLocation(12, 9), // (13,9): error CS8331: Cannot assign to field 'S.F2' because it is a readonly variable // s.F2 = t; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F2").WithArguments("field", "S.F2").WithLocation(13, 9), - // (14,9): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable - // s.F3 = t; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "s.F3").WithArguments("variable", "in S").WithLocation(14, 9), // (15,9): error CS8331: Cannot assign to field 'S.F4' because it is a readonly variable // s.F4 = t; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F4").WithArguments("field", "S.F4").WithLocation(15, 9)); @@ -1282,15 +1352,9 @@ static void M2(in S s2) }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (12,24): error CS8330: Members of variable 'in S' cannot be used as a ref or out value because it is a readonly variable - // ref T t1 = ref s1.F1; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "s1.F1").WithArguments("variable", "in S").WithLocation(12, 24), // (13,24): error CS8329: Cannot use field 'S.F2' as a ref or out value because it is a readonly variable // ref T t2 = ref s1.F2; Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s1.F2").WithArguments("field", "S.F2").WithLocation(13, 24), - // (14,24): error CS8330: Members of variable 'in S' cannot be used as a ref or out value because it is a readonly variable - // ref T t3 = ref s1.F3; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "s1.F3").WithArguments("variable", "in S").WithLocation(14, 24), // (15,24): error CS8329: Cannot use field 'S.F4' as a ref or out value because it is a readonly variable // ref T t4 = ref s1.F4; Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s1.F4").WithArguments("field", "S.F4").WithLocation(15, 24)); @@ -1436,9 +1500,9 @@ static T Read(R2 r2) { return r2.R1.F; } - static T ReadIn(in R2 r2) + static T ReadIn(in R2 r2In) { - return r2.R1.F; + return r2In.R1.F; } }"; var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @@ -1448,14 +1512,13 @@ static T ReadIn(in R2 r2) ")); verifier.VerifyIL("Program.Read", @"{ - // Code size 22 (0x16) + // Code size 18 (0x12) .maxstack 1 - IL_0000: ldarg.0 - IL_0001: ldfld ""ref R1 R2.R1"" - IL_0006: ldobj ""R1"" - IL_000b: ldfld ""ref T R1.F"" - IL_0010: ldobj ""T"" - IL_0015: ret + IL_0000: ldarga.s V_0 + IL_0002: ldfld ""ref R1 R2.R1"" + IL_0007: ldfld ""ref T R1.F"" + IL_000c: ldobj ""T"" + IL_0011: ret }"); verifier.VerifyIL("Program.ReadIn", @"{ @@ -1693,11 +1756,17 @@ static void Main() static ref T RefReturn(in S s) => ref s.F; static ref readonly T RefReadonlyReturn(in S s) => ref s.F; }"; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (17,49): error CS8334: Members of variable 'in S' cannot be returned by writable reference because it is a readonly variable - // static ref T RefReturn(in S s) => ref s.F; - Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "s.F").WithArguments("variable", "in S").WithLocation(17, 49)); + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("2")); + var expectedIL = +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ret +}"; + verifier.VerifyIL("Program.RefReturn", expectedIL); + verifier.VerifyIL("Program.RefReadonlyReturn", expectedIL); } [Fact] diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs index 1ce5b7b407866..0b776e67b1612 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs @@ -201,9 +201,9 @@ Cci.ITypeReference Cci.IFieldReference.GetType(EmitContext context) return UnderlyingField.GetType(context); } - ImmutableArray Cci.IFieldReference.RefCustomModifiers => ImmutableArray.Empty; + ImmutableArray Cci.IFieldReference.RefCustomModifiers => UnderlyingField.RefCustomModifiers; - bool Cci.IFieldReference.IsByReference => false; + bool Cci.IFieldReference.IsByReference => UnderlyingField.IsByReference; Cci.IFieldDefinition Cci.IFieldReference.GetResolvedField(EmitContext context) { diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs index 94638937fae6b..6068d257f6cfe 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs @@ -117,6 +117,7 @@ public virtual void Visit(IFieldDefinition fieldDefinition) this.Visit(marshalling); } + this.Visit(fieldDefinition.RefCustomModifiers); this.Visit(fieldDefinition.GetType(Context)); } diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index ebd851f35d750..84c84e691ad49 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -3336,13 +3336,17 @@ private void SerializeParameterInformation(ParameterTypeEncoder encoder, IParame private void SerializeFieldSignature(IFieldReference fieldReference, BlobBuilder builder) { + Debug.Assert(fieldReference.RefCustomModifiers.Length == 0 || fieldReference.IsByReference); + var typeEncoder = new BlobEncoder(builder).FieldSignature(); + SerializeCustomModifiers(typeEncoder.CustomModifiers(), fieldReference.RefCustomModifiers); + // PROTOTYPE: Add 'bool isByRef' parameter to BlobEncoder.FieldSignature(). - // PROTOTYPE: What about RefCustomModifiers? if (fieldReference.IsByReference) { typeEncoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference); } + SerializeTypeReference(typeEncoder, fieldReference.GetType(Context)); } From 31df2a553f1d877557621274745083030c8804d5 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 21 Apr 2022 08:52:29 -0700 Subject: [PATCH 11/29] Misc. --- src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs | 2 +- src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index a2383709c76a8..2457906270d19 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -78,7 +78,7 @@ internal enum BindValueKind : ushort /// /// Expression can be the LHS of a ref-assign operation. /// Example: - /// ref local, ref parameter, out parameter + /// ref local, ref parameter, out parameter, ref field /// RefAssignable = 8 << ValueKindInsignificantBits, diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 84c84e691ad49..0bb67cdd457c9 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -3338,15 +3338,14 @@ private void SerializeFieldSignature(IFieldReference fieldReference, BlobBuilder { Debug.Assert(fieldReference.RefCustomModifiers.Length == 0 || fieldReference.IsByReference); + // If BlobEncoder provides a field encoder that supports IsByReference and RefCustomModifiers + // directly, use that (see https://github.com/dotnet/runtime/issues/68309). + SerializeCustomModifiers(new CustomModifiersEncoder(builder), fieldReference.RefCustomModifiers); var typeEncoder = new BlobEncoder(builder).FieldSignature(); - SerializeCustomModifiers(typeEncoder.CustomModifiers(), fieldReference.RefCustomModifiers); - - // PROTOTYPE: Add 'bool isByRef' parameter to BlobEncoder.FieldSignature(). if (fieldReference.IsByReference) { typeEncoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference); } - SerializeTypeReference(typeEncoder, fieldReference.GetType(Context)); } From 5d1454d116d1e475fc95233a4206f492642e9f0b Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Sat, 9 Apr 2022 21:18:05 -0700 Subject: [PATCH 12/29] Additional tests --- .../Test/Semantic/Semantics/RefFieldTests.cs | 1420 +++++++++++++---- 1 file changed, 1069 insertions(+), 351 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index ce2d68630544a..c88b6dc614f9c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -508,7 +508,7 @@ public S(ref T t) } [Fact] - public void Assignment_Ref() + public void AssignValueTo_InstanceMethod_RefField() { var source = @"ref struct S @@ -517,10 +517,6 @@ public void Assignment_Ref() public S(T tValue, ref T tRef, out T tOut, in T tIn) { tOut = default; - F = ref tValue; - F = ref tRef; - F = ref tOut; - F = ref tIn; F = tValue; F = tRef; F = tOut; @@ -530,9 +526,6 @@ object P { init { - F = ref GetValue(); - F = ref GetRef(); - F = ref GetRefReadonly(); F = GetValue(); F = GetRef(); F = GetRefReadonly(); @@ -541,53 +534,77 @@ object P static T GetValue() => throw null; static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; -} -class Program +}"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void AssignValueTo_InstanceMethod_RefReadonlyField() + { + var source = +@"ref struct S { - static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) + public ref readonly T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) { tOut = default; - s.F = ref tValue; - s.F = ref tRef; - s.F = ref tOut; - s.F = ref tIn; - s.F = tValue; - s.F = tRef; - s.F = tOut; - s.F = tIn; + F = tValue; // 1 + F = tRef; // 2 + F = tOut; // 3 + F = tIn; // 4 + } + object P + { + init + { + F = GetValue(); // 5 + F = GetRef(); // 6 + F = GetRefReadonly(); // 7 + } } + static T GetValue() => throw null; + static ref T GetRef() => throw null; + static ref readonly T GetRefReadonly() => throw null; }"; - // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics( - // (10,17): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable - // F = ref tIn; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(10, 17), - // (20,21): error CS1510: A ref or out value must be an assignable variable - // F = ref GetValue(); - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetValue()").WithLocation(20, 21), - // (22,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' because it is a readonly variable - // F = ref GetRefReadonly(); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(22, 21), - // (40,20): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable - // s.F = ref tIn; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(40, 20)); + // (7,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tValue; // 1 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(7, 9), + // (7,9): error CS0170: Use of possibly unassigned field 'F' + // F = tValue; // 1 + Diagnostic(ErrorCode.ERR_UseDefViolationField, "F").WithArguments("F").WithLocation(7, 9), + // (8,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tRef; // 2 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 9), + // (9,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tOut; // 3 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 9), + // (10,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tIn; // 4 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(10, 9), + // (16,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetValue(); // 5 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(16, 13), + // (17,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetRef(); // 6 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(17, 13), + // (18,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetRefReadonly(); // 7 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 13)); } [Fact] - public void Assignment_RefReadonly() + public void AssignValueTo_InstanceMethod_ReadonlyRefField() { var source = @"ref struct S { - public ref readonly T F; + public readonly ref T F; public S(T tValue, ref T tRef, out T tOut, in T tIn) { tOut = default; - F = ref tValue; - F = ref tRef; - F = ref tOut; - F = ref tIn; F = tValue; F = tRef; F = tOut; @@ -597,9 +614,6 @@ object P { init { - F = ref GetValue(); - F = ref GetRef(); - F = ref GetRefReadonly(); F = GetValue(); F = GetRef(); F = GetRefReadonly(); @@ -608,70 +622,111 @@ object P static T GetValue() => throw null; static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; -} -class Program +}"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void AssignValueTo_InstanceMethod_ReadonlyRefReadonlyField() + { + var source = +@"ref struct S { - static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) + public readonly ref readonly T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) { tOut = default; - s.F = ref tValue; - s.F = ref tRef; - s.F = ref tOut; - s.F = ref tIn; - s.F = tValue; - s.F = tRef; - s.F = tOut; - s.F = tIn; + F = tValue; // 1 + F = tRef; // 2 + F = tOut; // 3 + F = tIn; // 4 } + object P + { + init + { + F = GetValue(); // 5 + F = GetRef(); // 6 + F = GetRefReadonly(); // 7 + } + } + static T GetValue() => throw null; + static ref T GetRef() => throw null; + static ref readonly T GetRefReadonly() => throw null; +}"; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics( + // (7,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tValue; // 1 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(7, 9), + // (7,9): error CS0170: Use of possibly unassigned field 'F' + // F = tValue; // 1 + Diagnostic(ErrorCode.ERR_UseDefViolationField, "F").WithArguments("F").WithLocation(7, 9), + // (8,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tRef; // 2 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 9), + // (9,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tOut; // 3 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 9), + // (10,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = tIn; // 4 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(10, 9), + // (16,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetValue(); // 5 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(16, 13), + // (17,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetRef(); // 6 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(17, 13), + // (18,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // F = GetRefReadonly(); // 7 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 13)); + } + + [Fact] + public void AssignRefTo_InstanceMethod_RefField() + { + var source = +@"ref struct S +{ + public ref T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + F = ref tValue; + F = ref tRef; + F = ref tOut; + F = ref tIn; // 1 + } + object P + { + init + { + F = ref GetRef(); + F = ref GetRefReadonly(); // 2 + } + } + static ref T GetRef() => throw null; + static ref readonly T GetRefReadonly() => throw null; }"; // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics( - // (11,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = tValue; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(11, 9), - // (12,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = tRef; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(12, 9), - // (13,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = tOut; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(13, 9), - // (14,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = tIn; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(14, 9), - // (20,21): error CS1510: A ref or out value must be an assignable variable - // F = ref GetValue(); - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetValue()").WithLocation(20, 21), - // (23,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = GetValue(); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(23, 13), - // (24,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = GetRef(); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(24, 13), - // (25,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = GetRefReadonly(); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(25, 13), - // (41,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // s.F = tValue; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(41, 9), - // (42,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // s.F = tRef; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(42, 9), - // (43,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // s.F = tOut; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(43, 9), - // (44,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // s.F = tIn; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 9)); + // (10,17): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // F = ref tIn; // 1 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(10, 17), + // (17,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' because it is a readonly variable + // F = ref GetRefReadonly(); // 2 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(17, 21)); } [Fact] - public void Assignment_ReadonlyRef() + public void AssignRefTo_InstanceMethod_RefReadonlyField() { var source = @"ref struct S { - public readonly ref T F; + public ref readonly T F; public S(T tValue, ref T tRef, out T tOut, in T tIn) { tOut = default; @@ -679,70 +734,62 @@ public S(T tValue, ref T tRef, out T tOut, in T tIn) F = ref tRef; F = ref tOut; F = ref tIn; - F = tValue; - F = tRef; - F = tOut; - F = tIn; } object P { init { - F = ref GetValue(); F = ref GetRef(); F = ref GetRefReadonly(); - F = GetValue(); - F = GetRef(); - F = GetRefReadonly(); } } - static T GetValue() => throw null; static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; -} -class Program +}"; + // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void AssignRefTo_InstanceMethod_ReadonlyRefField() + { + var source = +@"ref struct S { - static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) + public readonly ref T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) { tOut = default; - s.F = ref tValue; - s.F = ref tRef; - s.F = ref tOut; - s.F = ref tIn; - s.F = tValue; - s.F = tRef; - s.F = tOut; - s.F = tIn; + F = ref tValue; + F = ref tRef; + F = ref tOut; + F = ref tIn; // 1 } + object P + { + init + { + F = ref GetRef(); + F = ref GetRefReadonly(); // 2 + } + } + static ref T GetRef() => throw null; + static ref readonly T GetRefReadonly() => throw null; }"; // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics( // (10,17): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable - // F = ref tIn; + // F = ref tIn; // 1 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(10, 17), - // (20,21): error CS1510: A ref or out value must be an assignable variable - // F = ref GetValue(); - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetValue()").WithLocation(20, 21), - // (22,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' because it is a readonly variable - // F = ref GetRefReadonly(); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(22, 21), - // (37,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = ref tValue; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(37, 9), - // (38,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = ref tRef; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(38, 9), - // (39,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = ref tOut; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(39, 9), - // (40,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = ref tIn; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(40, 9)); + // (17,21): error CS8331: Cannot assign to method 'S.GetRefReadonly()' because it is a readonly variable + // F = ref GetRefReadonly(); // 2 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "GetRefReadonly()").WithArguments("method", "S.GetRefReadonly()").WithLocation(17, 21)); } [Fact] - public void Assignment_ReadonlyRefReadonly() + public void AssignRefTo_InstanceMethod_ReadonlyRefReadonlyField() { var source = @"ref struct S @@ -755,93 +802,670 @@ public S(T tValue, ref T tRef, out T tOut, in T tIn) F = ref tRef; F = ref tOut; F = ref tIn; - F = tValue; - F = tRef; - F = tOut; - F = tIn; } object P { init { - F = ref GetValue(); F = ref GetRef(); F = ref GetRefReadonly(); - F = GetValue(); - F = GetRef(); - F = GetRefReadonly(); } } - static T GetValue() => throw null; static ref T GetRef() => throw null; static ref readonly T GetRefReadonly() => throw null; +}"; + // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void AssignValueTo_RefField() + { + var source = +@"ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } } + class Program { - static void Assign(S s, T tValue, ref T tRef, out T tOut, in T tIn) - { - tOut = default; - s.F = ref tValue; - s.F = ref tRef; - s.F = ref tOut; - s.F = ref tIn; - s.F = tValue; - s.F = tRef; - s.F = tOut; - s.F = tIn; - } + static void AssignValueToValue(S s, T tValue) { s.F = tValue; } + static void AssignRefToValue(S s, ref T tRef) { s.F = tRef; } + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = tOut; } + static void AssignInToValue(S s, in T tIn) { s.F = tIn; } + + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = tValue; } + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = tRef; } + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } + + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } + + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } }"; - // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; - var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void AssignValueTo_RefReadonlyField() + { + var source = +@"ref struct S +{ + public ref readonly T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = tValue; } // 1 + static void AssignRefToValue(S s, ref T tRef) { s.F = tRef; } // 2 + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = tOut; } // 3 + static void AssignInToValue(S s, in T tIn) { s.F = tIn; } // 4 + + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = tValue; } // 5 + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = tRef; } // 6 + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } // 7 + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } // 8 + + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } // 9 + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } // 10 + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } // 11 + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } // 12 + + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } // 13 + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } // 14 + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } // 15 + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } // 16 +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,59): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignValueToValue(S s, T tValue) { s.F = tValue; } // 1 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 59), + // (10,59): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignRefToValue(S s, ref T tRef) { s.F = tRef; } // 2 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 59), + // (11,75): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = tOut; } // 3 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 75), + // (12,59): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignInToValue(S s, in T tIn) { s.F = tIn; } // 4 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 59), + // (14,64): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = tValue; } // 5 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(14, 64), + // (15,64): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = tRef; } // 6 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(15, 64), + // (16,80): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } // 7 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(16, 80), + // (17,64): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } // 8 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(17, 64), + // (19,80): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } // 9 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(19, 80), + // (20,80): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } // 10 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(20, 80), + // (21,96): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } // 11 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(21, 96), + // (22,80): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } // 12 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(22, 80), + // (24,61): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } // 13 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(24, 61), + // (25,61): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } // 14 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(25, 61), + // (26,77): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } // 15 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(26, 77), + // (27,61): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } // 16 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(27, 61)); + } + + [Fact] + public void AssignValueTo_ReadonlyRefField() + { + var source = +@"ref struct S +{ + public readonly ref T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = tValue; } + static void AssignRefToValue(S s, ref T tRef) { s.F = tRef; } + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = tOut; } + static void AssignInToValue(S s, in T tIn) { s.F = tIn; } + + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = tValue; } + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = tRef; } + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } + + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } + + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void AssignValueTo_ReadonlyRefReadonlyField() + { + var source = +@"ref struct S +{ + public readonly ref readonly T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = tValue; } // 1 + static void AssignRefToValue(S s, ref T tRef) { s.F = tRef; } // 2 + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = tOut; } // 3 + static void AssignInToValue(S s, in T tIn) { s.F = tIn; } // 4 + + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = tValue; } // 5 + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = tRef; } // 6 + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } // 7 + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } // 8 + + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } // 9 + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } // 10 + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } // 11 + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } // 12 + + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } // 13 + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } // 14 + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } // 15 + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } // 16 +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,59): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignValueToValue(S s, T tValue) { s.F = tValue; } // 1 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(9, 59), + // (10,59): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignRefToValue(S s, ref T tRef) { s.F = tRef; } // 2 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(10, 59), + // (11,75): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = tOut; } // 3 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(11, 75), + // (12,59): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignInToValue(S s, in T tIn) { s.F = tIn; } // 4 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(12, 59), + // (14,64): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = tValue; } // 5 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(14, 64), + // (15,64): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = tRef; } // 6 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(15, 64), + // (16,80): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } // 7 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(16, 80), + // (17,64): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } // 8 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sRef.F").WithArguments("field", "S.F").WithLocation(17, 64), + // (19,80): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } // 9 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(19, 80), + // (20,80): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } // 10 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(20, 80), + // (21,96): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } // 11 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(21, 96), + // (22,80): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } // 12 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sOut.F").WithArguments("field", "S.F").WithLocation(22, 80), + // (24,61): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } // 13 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(24, 61), + // (25,61): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } // 14 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(25, 61), + // (26,77): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } // 15 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(26, 77), + // (27,61): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } // 16 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "sIn.F").WithArguments("field", "S.F").WithLocation(27, 61)); + } + + [Fact] + public void AssignRefTo_RefField() + { + var source = +@"ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } + static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } + static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 1 + + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 2 + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 3 + + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 4 + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 5 + + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 6 + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 7 +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (12,69): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 1 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(12, 69), + // (14,64): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. + // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 2 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sRef.F = ref tValue").WithArguments("F", "tValue").WithLocation(14, 64), + // (17,77): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 3 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(17, 77), + // (19,80): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. + // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 4 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tValue").WithArguments("F", "tValue").WithLocation(19, 80), + // (22,93): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 5 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(22, 93), + // (24,61): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. + // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 6 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sIn.F = ref tValue").WithArguments("F", "tValue").WithLocation(24, 61), + // (27,73): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 7 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(27, 73)); + } + + [Fact] + public void AssignRefTo_RefReadonlyField() + { + var source = +@"ref struct S +{ + public ref readonly T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } + static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } + static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } + + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 1 + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } + + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 2 + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } + + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 3 + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (14,64): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. + // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 1 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sRef.F = ref tValue").WithArguments("F", "tValue").WithLocation(14, 64), + // (19,80): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. + // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 2 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tValue").WithArguments("F", "tValue").WithLocation(19, 80), + // (24,61): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. + // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 3 + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sIn.F = ref tValue").WithArguments("F", "tValue").WithLocation(24, 61)); + } + + [Fact] + public void AssignRefTo_ReadonlyRefField() + { + var source = +@"ref struct S +{ + public readonly ref T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 + static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } // 2 + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 3 + static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 4 + + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 5 + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } // 6 + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 7 + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 8 + + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 9 + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } // 10 + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 11 + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 12 + + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 13 + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 14 + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 15 + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 16 +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,59): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(9, 59), + // (10,59): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } // 2 + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(10, 59), + // (11,75): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 3 + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(11, 75), + // (12,59): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 4 + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(12, 59), + // (14,64): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 5 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sRef.F").WithLocation(14, 64), + // (15,64): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } // 6 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sRef.F").WithLocation(15, 64), + // (16,80): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 7 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sRef.F").WithLocation(16, 80), + // (17,64): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 8 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sRef.F").WithLocation(17, 64), + // (19,80): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 9 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sOut.F").WithLocation(19, 80), + // (20,80): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } // 10 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sOut.F").WithLocation(20, 80), + // (21,96): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 11 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sOut.F").WithLocation(21, 96), + // (22,80): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 12 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sOut.F").WithLocation(22, 80), + // (24,61): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 13 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sIn.F").WithLocation(24, 61), + // (25,61): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 14 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sIn.F").WithLocation(25, 61), + // (26,77): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 15 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sIn.F").WithLocation(26, 77), + // (27,61): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 16 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sIn.F").WithLocation(27, 61)); + } + + [Fact] + public void AssignRefTo_ReadonlyRefReadonlyField() + { + var source = +@"ref struct S +{ + public readonly ref readonly T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 + static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } // 2 + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 3 + static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 4 + + static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 5 + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } // 6 + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 7 + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 8 + + static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 9 + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } // 10 + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 11 + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 12 + + static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 13 + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 14 + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 15 + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 16 +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,59): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } // 1 + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(9, 59), + // (10,59): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } // 2 + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(10, 59), + // (11,75): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } // 3 + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(11, 75), + // (12,59): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } // 4 + Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(12, 59), + // (14,64): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignValueToRef(ref S sRef, T tValue) { sRef.F = ref tValue; } // 5 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sRef.F").WithLocation(14, 64), + // (15,64): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } // 6 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sRef.F").WithLocation(15, 64), + // (16,80): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } // 7 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sRef.F").WithLocation(16, 80), + // (17,64): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } // 8 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sRef.F").WithLocation(17, 64), + // (19,80): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 9 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sOut.F").WithLocation(19, 80), + // (20,80): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } // 10 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sOut.F").WithLocation(20, 80), + // (21,96): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } // 11 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sOut.F").WithLocation(21, 96), + // (22,80): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 12 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sOut.F").WithLocation(22, 80), + // (24,61): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 13 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sIn.F").WithLocation(24, 61), + // (25,61): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 14 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sIn.F").WithLocation(25, 61), + // (26,77): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 15 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sIn.F").WithLocation(26, 77), + // (27,61): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 16 + Diagnostic(ErrorCode.ERR_AssgReadonly, "sIn.F").WithLocation(27, 61)); + } + + [Fact] + public void AssignOutFrom_RefField() + { + var source = +@"#pragma warning disable 649 +ref struct S +{ + public ref T Ref; + public ref readonly T RefReadonly; + public readonly ref T ReadonlyRef; + public readonly ref readonly T ReadonlyRefReadonly; +} + +class Program +{ + static void FromValueRef(S s, out T t) { t = s.Ref; } + static void FromValueRefReadonly(S s, out T t) { t = s.RefReadonly; } + static void FromValueReadonlyRef(S s, out T t) { t = s.ReadonlyRef; } + static void FromValueReadonlyRefReadonly(S s, out T t) { t = s.ReadonlyRefReadonly; } + + static void FromRefRef(ref S s, out T t) { t = s.Ref; } + static void FromRefRefReadonly(ref S s, out T t) { t = s.RefReadonly; } + static void FromRefReadonlyRef(ref S s, out T t) { t = s.ReadonlyRef; } + static void FromRefReadonlyRefReadonly(ref S s, out T t) { t = s.ReadonlyRefReadonly; } + + static void FromOutRef(out S s, out T t) { s = default; t = s.Ref; } + static void FromOutRefReadonly(out S s, out T t) { s = default; t = s.RefReadonly; } + static void FromOutReadonlyRef(out S s, out T t) { s = default; t = s.ReadonlyRef; } + static void FromOutReadonlyRefReadonly(out S s, out T t) { s = default; t = s.ReadonlyRefReadonly; } + + static void FromInRef(in S s, out T t) { t = s.Ref; } + static void FromInRefReadonly(in S s, out T t) { t = s.RefReadonly; } + static void FromInReadonlyRef(in S s, out T t) { t = s.ReadonlyRef; } + static void FromInReadonlyRefReadonly(in S s, out T t) { t = s.ReadonlyRefReadonly; } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void AssignRefFrom_RefField() + { + var source = +@"ref struct S +{ + public ref T Ref; + public ref readonly T RefReadonly; + public readonly ref T ReadonlyRef; + public readonly ref readonly T ReadonlyRefReadonly; +} + +class Program +{ + static void FromValueRef(S s) { ref T t = ref s.Ref; } + static void FromValueRefReadonly(S s) { ref T t = ref s.RefReadonly; } // 1 + static void FromValueReadonlyRef(S s) { ref T t = ref s.ReadonlyRef; } + static void FromValueReadonlyRefReadonly(S s) { ref T t = ref s.ReadonlyRefReadonly; } // 2 + + static void FromRefRef(ref S s) { ref T t = ref s.Ref; } + static void FromRefRefReadonly(ref S s) { ref T t = ref s.RefReadonly; } // 3 + static void FromRefReadonlyRef(ref S s) { ref T t = ref s.ReadonlyRef; } + static void FromRefReadonlyRefReadonly(ref S s) { ref T t = ref s.ReadonlyRefReadonly; } // 4 + + static void FromOutRef(out S s) { s = default; ref T t = ref s.Ref; } + static void FromOutRefReadonly(out S s) { s = default; ref T t = ref s.RefReadonly; } // 5 + static void FromOutReadonlyRef(out S s) { s = default; ref T t = ref s.ReadonlyRef; } + static void FromOutReadonlyRefReadonly(out S s) { s = default; ref T t = ref s.ReadonlyRefReadonly; } // 6 + + static void FromInRef(in S s) { ref T t = ref s.Ref; } + static void FromInRefReadonly(in S s) { ref T t = ref s.RefReadonly; } // 7 + static void FromInReadonlyRef(in S s) { ref T t = ref s.ReadonlyRef; } + static void FromInReadonlyRefReadonly(in S s) { ref T t = ref s.ReadonlyRefReadonly; } // 8 +}"; + var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (11,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = tValue; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(11, 9), - // (12,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = tRef; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(12, 9), - // (13,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = tOut; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(13, 9), - // (14,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = tIn; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(14, 9), - // (20,21): error CS1510: A ref or out value must be an assignable variable - // F = ref GetValue(); - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetValue()").WithLocation(20, 21), - // (23,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = GetValue(); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(23, 13), - // (24,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = GetRef(); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(24, 13), - // (25,13): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // F = GetRefReadonly(); - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(25, 13), - // (37,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = ref tValue; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(37, 9), - // (38,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = ref tRef; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(38, 9), - // (39,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = ref tOut; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(39, 9), - // (40,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) - // s.F = ref tIn; - Diagnostic(ErrorCode.ERR_AssgReadonly, "s.F").WithLocation(40, 9), - // (41,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // s.F = tValue; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(41, 9), - // (42,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // s.F = tRef; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(42, 9), - // (43,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // s.F = tOut; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(43, 9), - // (44,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable - // s.F = tIn; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 9)); + // (12,73): error CS8329: Cannot use field 'S.RefReadonly' as a ref or out value because it is a readonly variable + // static void FromValueRefReadonly(S s) { ref T t = ref s.RefReadonly; } // 1 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "S.RefReadonly").WithLocation(12, 73), + // (14,73): error CS8329: Cannot use field 'S.ReadonlyRefReadonly' as a ref or out value because it is a readonly variable + // static void FromValueReadonlyRefReadonly(S s) { ref T t = ref s.ReadonlyRefReadonly; } // 2 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "S.ReadonlyRefReadonly").WithLocation(14, 73), + // (17,75): error CS8329: Cannot use field 'S.RefReadonly' as a ref or out value because it is a readonly variable + // static void FromRefRefReadonly(ref S s) { ref T t = ref s.RefReadonly; } // 3 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "S.RefReadonly").WithLocation(17, 75), + // (19,75): error CS8329: Cannot use field 'S.ReadonlyRefReadonly' as a ref or out value because it is a readonly variable + // static void FromRefReadonlyRefReadonly(ref S s) { ref T t = ref s.ReadonlyRefReadonly; } // 4 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "S.ReadonlyRefReadonly").WithLocation(19, 75), + // (22,88): error CS8329: Cannot use field 'S.RefReadonly' as a ref or out value because it is a readonly variable + // static void FromOutRefReadonly(out S s) { s = default; ref T t = ref s.RefReadonly; } // 5 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "S.RefReadonly").WithLocation(22, 88), + // (24,88): error CS8329: Cannot use field 'S.ReadonlyRefReadonly' as a ref or out value because it is a readonly variable + // static void FromOutReadonlyRefReadonly(out S s) { s = default; ref T t = ref s.ReadonlyRefReadonly; } // 6 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "S.ReadonlyRefReadonly").WithLocation(24, 88), + // (27,73): error CS8329: Cannot use field 'S.RefReadonly' as a ref or out value because it is a readonly variable + // static void FromInRefReadonly(in S s) { ref T t = ref s.RefReadonly; } // 7 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.RefReadonly").WithArguments("field", "S.RefReadonly").WithLocation(27, 73), + // (29,73): error CS8329: Cannot use field 'S.ReadonlyRefReadonly' as a ref or out value because it is a readonly variable + // static void FromInReadonlyRefReadonly(in S s) { ref T t = ref s.ReadonlyRefReadonly; } // 8 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.ReadonlyRefReadonly").WithArguments("field", "S.ReadonlyRefReadonly").WithLocation(29, 73)); + } + + [Fact] + public void AssignRefReadonlyFrom_RefField() + { + var source = +@"ref struct S +{ + public ref T Ref; + public ref readonly T RefReadonly; + public readonly ref T ReadonlyRef; + public readonly ref readonly T ReadonlyRefReadonly; +} + +class Program +{ + static void FromValueRef(S s) { ref readonly T t = ref s.Ref; } + static void FromValueRefReadonly(S s) { ref readonly T t = ref s.RefReadonly; } + static void FromValueReadonlyRef(S s) { ref readonly T t = ref s.ReadonlyRef; } + static void FromValueReadonlyRefReadonly(S s) { ref readonly T t = ref s.ReadonlyRefReadonly; } + + static void FromRefRef(ref S s) { ref readonly T t = ref s.Ref; } + static void FromRefRefReadonly(ref S s) { ref readonly T t = ref s.RefReadonly; } + static void FromRefReadonlyRef(ref S s) { ref readonly T t = ref s.ReadonlyRef; } + static void FromRefReadonlyRefReadonly(ref S s) { ref readonly T t = ref s.ReadonlyRefReadonly; } + + static void FromOutRef(out S s) { s = default; ref readonly T t = ref s.Ref; } + static void FromOutRefReadonly(out S s) { s = default; ref readonly T t = ref s.RefReadonly; } + static void FromOutReadonlyRef(out S s) { s = default; ref readonly T t = ref s.ReadonlyRef; } + static void FromOutReadonlyRefReadonly(out S s) { s = default; ref readonly T t = ref s.ReadonlyRefReadonly; } + + static void FromInRef(in S s) { ref readonly T t = ref s.Ref; } + static void FromInRefReadonly(in S s) { ref readonly T t = ref s.RefReadonly; } + static void FromInReadonlyRef(in S s) { ref readonly T t = ref s.ReadonlyRef; } + static void FromInReadonlyRefReadonly(in S s) { ref readonly T t = ref s.ReadonlyRefReadonly; } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -1019,7 +1643,7 @@ class Program } [Fact] - public void RefParameter_Ref() + public void RefParameter_InstanceMethod_Ref() { var source = @"ref struct S @@ -1053,29 +1677,17 @@ void M() M4(F); M4(in F); } - public static void M1(T t) { } - public static void M2(ref T t) { } - public static void M3(out T t) { t = default; } - public static void M4(in T t) { } -} -class Program -{ - static void M() - { - var s = new S(); - S.M1(s.F); - S.M2(ref s.F); - S.M3(out s.F); - S.M4(s.F); - S.M4(in s.F); - } + static void M1(T t) { } + static void M2(ref T t) { } + static void M3(out T t) { t = default; } + static void M4(in T t) { } }"; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics(); } [Fact] - public void RefParameter_RefReadonly() + public void RefParameter_InstanceMethod_RefReadonly() { var source = @"ref struct S @@ -1085,8 +1697,8 @@ public S(ref T t) { F = ref t; M1(F); - M2(ref F); - M3(out F); + M2(ref F); // 1 + M3(out F); // 2 M4(F); M4(in F); } @@ -1095,8 +1707,8 @@ object P init { M1(F); - M2(ref F); - M3(out F); + M2(ref F); // 3 + M3(out F); // 4 M4(F); M4(in F); } @@ -1104,58 +1716,40 @@ object P void M() { M1(F); - M2(ref F); - M3(out F); + M2(ref F); // 5 + M3(out F); // 6 M4(F); M4(in F); } - public static void M1(T t) { } - public static void M2(ref T t) { } - public static void M3(out T t) { t = default; } - public static void M4(in T t) { } -} -class Program -{ - static void M() - { - var s = new S(); - S.M1(s.F); - S.M2(ref s.F); - S.M3(out s.F); - S.M4(s.F); - S.M4(in s.F); - } + static void M1(T t) { } + static void M2(ref T t) { } + static void M3(out T t) { t = default; } + static void M4(in T t) { } }"; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics( // (8,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M2(ref F); + // M2(ref F); // 1 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 16), // (9,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M3(out F); + // M3(out F); // 2 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 16), // (18,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M2(ref F); + // M2(ref F); // 3 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 20), // (19,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M3(out F); + // M3(out F); // 4 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(19, 20), // (27,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M2(ref F); + // M2(ref F); // 5 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(27, 16), // (28,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M3(out F); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16), - // (43,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // S.M2(ref s.F); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(43, 21), - // (44,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // S.M3(out s.F); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 21)); + // M3(out F); // 6 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16)); } [Fact] - public void RefParameter_ReadonlyRef() + public void RefParameter_InstanceMethod_ReadonlyRef() { var source = @"ref struct S @@ -1189,30 +1783,17 @@ void M() M4(F); M4(in F); } - public static void M1(T t) { } - public static void M2(ref T t) { } - public static void M3(out T t) { t = default; } - public static void M4(in T t) { } -} -class Program -{ - static void M() - { - var s = new S(); - S.M1(s.F); - S.M2(ref s.F); - S.M3(out s.F); - S.M4(s.F); - S.M4(in s.F); - } + static void M1(T t) { } + static void M2(ref T t) { } + static void M3(out T t) { t = default; } + static void M4(in T t) { } }"; - // PROTOTYPE: Execute code and verify IL for each of the cases. var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics(); } [Fact] - public void RefParameter_ReadonlyRefReadonly() + public void RefParameter_InstanceMethod_ReadonlyRefReadonly() { var source = @"ref struct S @@ -1222,8 +1803,8 @@ public S(ref T t) { F = ref t; M1(F); - M2(ref F); - M3(out F); + M2(ref F); // 1 + M3(out F); // 2 M4(F); M4(in F); } @@ -1232,8 +1813,8 @@ object P init { M1(F); - M2(ref F); - M3(out F); + M2(ref F); // 3 + M3(out F); // 4 M4(F); M4(in F); } @@ -1241,123 +1822,260 @@ object P void M() { M1(F); - M2(ref F); - M3(out F); + M2(ref F); // 5 + M3(out F); // 6 M4(F); M4(in F); } - public static void M1(T t) { } - public static void M2(ref T t) { } - public static void M3(out T t) { t = default; } - public static void M4(in T t) { } -} -class Program -{ - static void M() - { - var s = new S(); - S.M1(s.F); - S.M2(ref s.F); - S.M3(out s.F); - S.M4(s.F); - S.M4(in s.F); - } + static void M1(T t) { } + static void M2(ref T t) { } + static void M3(out T t) { t = default; } + static void M4(in T t) { } }"; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics( // (8,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M2(ref F); + // M2(ref F); // 1 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 16), // (9,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M3(out F); + // M3(out F); // 2 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(9, 16), // (18,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M2(ref F); + // M2(ref F); // 3 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(18, 20), // (19,20): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M3(out F); + // M3(out F); // 4 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(19, 20), // (27,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M2(ref F); + // M2(ref F); // 5 Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(27, 16), // (28,16): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // M3(out F); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16), - // (43,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // S.M2(ref s.F); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(43, 21), - // (44,21): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable - // S.M3(out s.F); - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(44, 21)); + // M3(out F); // 6 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16)); } [Fact] - public void AssignToReadonlyStruct() + public void RefParameter_Ref() { var source = @"ref struct S { - public ref T F1; - public ref readonly T F2; - public readonly ref T F3; - public readonly ref readonly T F4; + public ref T F; } + class Program { - static void M(in S s, T t) - { - s.F1 = t; - s.F2 = t; - s.F3 = t; - s.F4 = t; - } + static void M1(T t) { } + static void M2(ref T t) { } + static void M3(out T t) { t = default; } + static void M4(in T t) { } + + static void FromValue1(S s) { M1(s.F); } + static void FromValue2(S s) { M2(ref s.F); } + static void FromValue3(S s) { M3(out s.F); } + static void FromValue4A(S s) { M4(in s.F); } + static void FromValue4B(S s) { M4(s.F); } + + static void FromRef1(ref S s) { M1(s.F); } + static void FromRef2(ref S s) { M2(ref s.F); } + static void FromRef3(ref S s) { M3(out s.F); } + static void FromRef4A(ref S s) { M4(in s.F); } + static void FromRef4B(ref S s) { M4(s.F); } + + static void FromOut1(out S s) { s = default; M1(s.F); } + static void FromOut2(out S s) { s = default; M2(ref s.F); } + static void FromOut3(out S s) { s = default; M3(out s.F); } + static void FromOut4A(out S s) { s = default; M4(in s.F); } + static void FromOut4B(out S s) { s = default; M4(s.F); } + + static void FromIn1(in S s) { M1(s.F); } + static void FromIn2(in S s) { M2(ref s.F); } + static void FromIn3(in S s) { M3(out s.F); } + static void FromIn4A(in S s) { M4(in s.F); } + static void FromIn4B(in S s) { M4(s.F); } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void RefParameter_RefReadonly() + { + var source = +@"ref struct S +{ + public ref readonly T F; +} + +class Program +{ + static void M1(T t) { } + static void M2(ref T t) { } + static void M3(out T t) { t = default; } + static void M4(in T t) { } + + static void FromValue1(S s) { M1(s.F); } + static void FromValue2(S s) { M2(ref s.F); } // 1 + static void FromValue3(S s) { M3(out s.F); } // 2 + static void FromValue4A(S s) { M4(in s.F); } + static void FromValue4B(S s) { M4(s.F); } + + static void FromRef1(ref S s) { M1(s.F); } + static void FromRef2(ref S s) { M2(ref s.F); } // 3 + static void FromRef3(ref S s) { M3(out s.F); } // 4 + static void FromRef4A(ref S s) { M4(in s.F); } + static void FromRef4B(ref S s) { M4(s.F); } + + static void FromOut1(out S s) { s = default; M1(s.F); } + static void FromOut2(out S s) { s = default; M2(ref s.F); } // 5 + static void FromOut3(out S s) { s = default; M3(out s.F); } // 6 + static void FromOut4A(out S s) { s = default; M4(in s.F); } + static void FromOut4B(out S s) { s = default; M4(s.F); } + + static void FromIn1(in S s) { M1(s.F); } + static void FromIn2(in S s) { M2(ref s.F); } // 7 + static void FromIn3(in S s) { M3(out s.F); } // 8 + static void FromIn4A(in S s) { M4(in s.F); } + static void FromIn4B(in S s) { M4(s.F); } }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (13,9): error CS8331: Cannot assign to field 'S.F2' because it is a readonly variable - // s.F2 = t; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F2").WithArguments("field", "S.F2").WithLocation(13, 9), - // (15,9): error CS8331: Cannot assign to field 'S.F4' because it is a readonly variable - // s.F4 = t; - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "s.F4").WithArguments("field", "S.F4").WithLocation(15, 9)); + // (14,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromValue2(S s) { M2(ref s.F); } // 1 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(14, 49), + // (15,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromValue3(S s) { M3(out s.F); } // 2 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(15, 49), + // (20,51): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromRef2(ref S s) { M2(ref s.F); } // 3 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(20, 51), + // (21,51): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromRef3(ref S s) { M3(out s.F); } // 4 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(21, 51), + // (26,64): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromOut2(out S s) { s = default; M2(ref s.F); } // 5 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(26, 64), + // (27,64): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromOut3(out S s) { s = default; M3(out s.F); } // 6 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(27, 64), + // (32,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromIn2(in S s) { M2(ref s.F); } // 7 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(32, 49), + // (33,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromIn3(in S s) { M3(out s.F); } // 8 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(33, 49)); } [Fact] - public void RefAssignFromReadonlyStruct() + public void RefParameter_ReadonlyRef() { var source = @"ref struct S { - public ref T F1; - public ref readonly T F2; - public readonly ref T F3; - public readonly ref readonly T F4; + public readonly ref T F; } + class Program { - static void M1(in S s1) - { - ref T t1 = ref s1.F1; - ref T t2 = ref s1.F2; - ref T t3 = ref s1.F3; - ref T t4 = ref s1.F4; - } - static void M2(in S s2) - { - ref readonly T t1 = ref s2.F1; - ref readonly T t2 = ref s2.F2; - ref readonly T t3 = ref s2.F3; - ref readonly T t4 = ref s2.F4; - } + static void M1(T t) { } + static void M2(ref T t) { } + static void M3(out T t) { t = default; } + static void M4(in T t) { } + + static void FromValue1(S s) { M1(s.F); } + static void FromValue2(S s) { M2(ref s.F); } + static void FromValue3(S s) { M3(out s.F); } + static void FromValue4A(S s) { M4(in s.F); } + static void FromValue4B(S s) { M4(s.F); } + + static void FromRef1(ref S s) { M1(s.F); } + static void FromRef2(ref S s) { M2(ref s.F); } + static void FromRef3(ref S s) { M3(out s.F); } + static void FromRef4A(ref S s) { M4(in s.F); } + static void FromRef4B(ref S s) { M4(s.F); } + + static void FromOut1(out S s) { s = default; M1(s.F); } + static void FromOut2(out S s) { s = default; M2(ref s.F); } + static void FromOut3(out S s) { s = default; M3(out s.F); } + static void FromOut4A(out S s) { s = default; M4(in s.F); } + static void FromOut4B(out S s) { s = default; M4(s.F); } + + static void FromIn1(in S s) { M1(s.F); } + static void FromIn2(in S s) { M2(ref s.F); } + static void FromIn3(in S s) { M3(out s.F); } + static void FromIn4A(in S s) { M4(in s.F); } + static void FromIn4B(in S s) { M4(s.F); } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void RefParameter_ReadonlyRefReadonly() + { + var source = +@"ref struct S +{ + public readonly ref readonly T F; +} + +class Program +{ + static void M1(T t) { } + static void M2(ref T t) { } + static void M3(out T t) { t = default; } + static void M4(in T t) { } + + static void FromValue1(S s) { M1(s.F); } + static void FromValue2(S s) { M2(ref s.F); } // 1 + static void FromValue3(S s) { M3(out s.F); } // 2 + static void FromValue4A(S s) { M4(in s.F); } + static void FromValue4B(S s) { M4(s.F); } + + static void FromRef1(ref S s) { M1(s.F); } + static void FromRef2(ref S s) { M2(ref s.F); } // 3 + static void FromRef3(ref S s) { M3(out s.F); } // 4 + static void FromRef4A(ref S s) { M4(in s.F); } + static void FromRef4B(ref S s) { M4(s.F); } + + static void FromOut1(out S s) { s = default; M1(s.F); } + static void FromOut2(out S s) { s = default; M2(ref s.F); } // 5 + static void FromOut3(out S s) { s = default; M3(out s.F); } // 6 + static void FromOut4A(out S s) { s = default; M4(in s.F); } + static void FromOut4B(out S s) { s = default; M4(s.F); } + + static void FromIn1(in S s) { M1(s.F); } + static void FromIn2(in S s) { M2(ref s.F); } // 7 + static void FromIn3(in S s) { M3(out s.F); } // 8 + static void FromIn4A(in S s) { M4(in s.F); } + static void FromIn4B(in S s) { M4(s.F); } }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (13,24): error CS8329: Cannot use field 'S.F2' as a ref or out value because it is a readonly variable - // ref T t2 = ref s1.F2; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s1.F2").WithArguments("field", "S.F2").WithLocation(13, 24), - // (15,24): error CS8329: Cannot use field 'S.F4' as a ref or out value because it is a readonly variable - // ref T t4 = ref s1.F4; - Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s1.F4").WithArguments("field", "S.F4").WithLocation(15, 24)); + // (14,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromValue2(S s) { M2(ref s.F); } // 1 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(14, 49), + // (15,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromValue3(S s) { M3(out s.F); } // 2 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(15, 49), + // (20,51): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromRef2(ref S s) { M2(ref s.F); } // 3 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(20, 51), + // (21,51): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromRef3(ref S s) { M3(out s.F); } // 4 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(21, 51), + // (26,64): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromOut2(out S s) { s = default; M2(ref s.F); } // 5 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(26, 64), + // (27,64): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromOut3(out S s) { s = default; M3(out s.F); } // 6 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(27, 64), + // (32,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromIn2(in S s) { M2(ref s.F); } // 7 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(32, 49), + // (33,49): error CS8329: Cannot use field 'S.F' as a ref or out value because it is a readonly variable + // static void FromIn3(in S s) { M3(out s.F); } // 8 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(33, 49)); } [Fact] From ccbd3651e469e411a58658729f02d43c78e8357e Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 21 Apr 2022 15:02:42 -0700 Subject: [PATCH 13/29] Fix tests --- .../CSharp/Test/Semantic/Semantics/RefFieldTests.cs | 7 +++---- src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index c88b6dc614f9c..3f546beb24ead 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -19,8 +19,7 @@ public class RefFieldTests : CSharpTestBase { private static string IncludeExpectedOutput(string expectedOutput) { - // PROTOTYPE: Enable. -#if RuntimeSupport +#if NET7_0_OR_GREATER return expectedOutput; #else return null; @@ -2573,8 +2572,8 @@ static void Subtract(S s, int offset) var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"43 43 -34 -34 +33 +33 ")); verifier.VerifyIL("Program.Increment", @"{ diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 0bb67cdd457c9..506992927a02f 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -3340,8 +3340,8 @@ private void SerializeFieldSignature(IFieldReference fieldReference, BlobBuilder // If BlobEncoder provides a field encoder that supports IsByReference and RefCustomModifiers // directly, use that (see https://github.com/dotnet/runtime/issues/68309). - SerializeCustomModifiers(new CustomModifiersEncoder(builder), fieldReference.RefCustomModifiers); var typeEncoder = new BlobEncoder(builder).FieldSignature(); + SerializeCustomModifiers(new CustomModifiersEncoder(builder), fieldReference.RefCustomModifiers); if (fieldReference.IsByReference) { typeEncoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference); From 67bb32dfaea49dfd7662098d9f494ce79611dd3b Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 21 Apr 2022 21:53:24 -0700 Subject: [PATCH 14/29] PR feedback --- .../Portable/Binder/Binder.ValueChecks.cs | 2 +- .../Metadata/PE/MemberRefMetadataDecoder.cs | 13 ++-- .../Symbols/Metadata/PE/PENamedTypeSymbol.cs | 4 +- .../Test/Semantic/Semantics/RefFieldTests.cs | 70 ++++++++++++++++++- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 2457906270d19..40e72d0c38bfc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -796,7 +796,7 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, // S has a mutable field x, then c.f.x is not a variable because c.f is not // writable. - if (RequiresAssignableVariable(valueKind) && fieldSymbol.RefKind == RefKind.None || + if ((RequiresAssignableVariable(valueKind) && fieldSymbol.RefKind == RefKind.None) || RequiresRefAssignableVariable(valueKind)) { var canModifyReadonly = false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs index edc352556b6cb..55f12302bde1a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs @@ -139,7 +139,7 @@ internal Symbol FindMember(MemberReferenceHandle memberRef, bool methodsOnly) } FieldInfo fieldInfo = this.DecodeFieldSignature(ref signaturePointer); - return FindFieldBySignature(_containingType, memberName, fieldInfo.CustomModifiers, fieldInfo.Type); + return FindFieldBySignature(_containingType, memberName, fieldInfo); default: // error: unexpected calling convention @@ -152,16 +152,21 @@ internal Symbol FindMember(MemberReferenceHandle memberRef, bool methodsOnly) } } - private static FieldSymbol FindFieldBySignature(TypeSymbol targetTypeSymbol, string targetMemberName, ImmutableArray> customModifiers, TypeSymbol type) + private static FieldSymbol FindFieldBySignature(TypeSymbol targetTypeSymbol, string targetMemberName, in FieldInfo fieldInfo) { foreach (Symbol member in targetTypeSymbol.GetMembers(targetMemberName)) { var field = member as FieldSymbol; TypeWithAnnotations fieldType; + // Ensure the field symbol matches the { IsByRef, RefCustomModifiers, Type, CustomModifiers } + // from metadata. The checks are not strictly necessary though since PENamedTypeSymbol + // does not support fields with duplicate names. Matching by name is sufficient. if ((object)field != null && - TypeSymbol.Equals((fieldType = field.TypeWithAnnotations).Type, type, TypeCompareKind.CLRSignatureCompareOptions) && - CustomModifiersMatch(fieldType.CustomModifiers, customModifiers)) + (field.RefKind != RefKind.None) == fieldInfo.IsByRef && + CustomModifiersMatch(field.RefCustomModifiers, fieldInfo.RefCustomModifiers) && + TypeSymbol.Equals((fieldType = field.TypeWithAnnotations).Type, fieldInfo.Type, TypeCompareKind.CLRSignatureCompareOptions) && + CustomModifiersMatch(fieldType.CustomModifiers, fieldInfo.CustomModifiers)) { // Behavior in the face of multiple matching signatures is // implementation defined - we'll just pick the first one. diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index 2f8eea7c3863e..dae9eb3b321b7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -1154,7 +1154,9 @@ private void EnsureEnumUnderlyingTypeIsLoaded(UncommonProperties uncommon) FieldInfo fieldInfo = decoder.DecodeFieldSignature(fieldDef); TypeSymbol type = fieldInfo.Type; - if (type.SpecialType.IsValidEnumUnderlyingType() && !fieldInfo.CustomModifiers.AnyRequired()) + if (type.SpecialType.IsValidEnumUnderlyingType() && + !fieldInfo.RefCustomModifiers.AnyRequired() && + !fieldInfo.CustomModifiers.AnyRequired()) { if ((object)underlyingType == null) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 3f546beb24ead..a152bc07706f5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -437,6 +437,70 @@ static void F(object o) Assert.Equal(new[] { "System.Int32", "System.Object" }, fieldInfo.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); } + [Fact] + public void MemberRefMetadataDecoder_FindFieldBySignature() + { + var sourceA = +@".class public sealed R extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .field public !0& modopt(object) F +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class Program +{ + static int Main() + { + var r = new R(); + return r.F; + } +}"; + var verifier = CompileAndVerify(sourceB, new[] { refA }); + // MemberRefMetadataDecoder.FindFieldBySignature() is used to find fields when realIL: true. + verifier.VerifyIL("Program.Main", realIL: true, expectedIL: +@"{ + // Code size 16 (0x10) + .maxstack 1 + .locals init (R V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj ""R"" + IL_0008: ldloc.0 + IL_0009: ldfld ""ref int R.F"" + IL_000e: ldind.i4 + IL_000f: ret +}"); + } + + [Fact] + public void EnumUnderlyingType() + { + var sourceA = +@".class public sealed E extends [mscorlib]System.Enum +{ + .field public int64 modreq(object) value1 + .field public int32& modreq(object) value2 + .field public int16 value3 + .field public static literal valuetype E A = int16(0x01) +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class Program +{ + static void Main() + { + _ = E.A; + } +}"; + var comp = CreateCompilation(sourceB, references: new[] { refA }); + comp.VerifyEmitDiagnostics(); + + var type = (NamedTypeSymbol)comp.GetTypeByMetadataName("E"); + Assert.Equal(SpecialType.System_Int16, type.EnumUnderlyingType.SpecialType); + } + private static void VerifyFieldSymbol(FieldSymbol field, string expectedDisplayString, RefKind expectedRefKind, string[] expectedRefCustomModifiers) { Assert.Equal(expectedRefKind, field.RefKind); @@ -1329,7 +1393,7 @@ class Program } [Fact] - public void AssignOutFrom_RefField() + public void AssignOutParameterFrom_RefField() { var source = @"#pragma warning disable 649 @@ -1368,7 +1432,7 @@ class Program } [Fact] - public void AssignRefFrom_RefField() + public void AssignRefLocalFrom_RefField() { var source = @"ref struct S @@ -1430,7 +1494,7 @@ class Program } [Fact] - public void AssignRefReadonlyFrom_RefField() + public void AssignRefReadonlyLocalFrom_RefField() { var source = @"ref struct S From de9cb6e3fb1b3341166aadebf9698d87e4fc0257 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Sat, 23 Apr 2022 07:19:51 -0700 Subject: [PATCH 15/29] Fix tests --- .../Test/Semantic/Semantics/RefFieldTests.cs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index a152bc07706f5..3c4f212f16d7b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -457,7 +457,7 @@ static int Main() return r.F; } }"; - var verifier = CompileAndVerify(sourceB, new[] { refA }); + var verifier = CompileAndVerify(sourceB, new[] { refA }, verify: Verification.Skipped); // MemberRefMetadataDecoder.FindFieldBySignature() is used to find fields when realIL: true. verifier.VerifyIL("Program.Main", realIL: true, expectedIL: @"{ @@ -546,10 +546,7 @@ static void Main() } }"; var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (8,12): error CS0171: Field 'S2.F' must be fully assigned before control is returned to the caller - // public S2(ref T t) { } - Diagnostic(ErrorCode.ERR_UnassignedThis, "S2").WithArguments("S2.F").WithLocation(8, 12)); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -635,9 +632,6 @@ object P // (7,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable // F = tValue; // 1 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(7, 9), - // (7,9): error CS0170: Use of possibly unassigned field 'F' - // F = tValue; // 1 - Diagnostic(ErrorCode.ERR_UseDefViolationField, "F").WithArguments("F").WithLocation(7, 9), // (8,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable // F = tRef; // 2 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 9), @@ -723,9 +717,6 @@ object P // (7,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable // F = tValue; // 1 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(7, 9), - // (7,9): error CS0170: Use of possibly unassigned field 'F' - // F = tValue; // 1 - Diagnostic(ErrorCode.ERR_UseDefViolationField, "F").WithArguments("F").WithLocation(7, 9), // (8,9): error CS8331: Cannot assign to field 'S.F' because it is a readonly variable // F = tRef; // 2 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(8, 9), @@ -2964,15 +2955,9 @@ static void Main() // (8,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // P = ref t; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "P").WithLocation(8, 9), - // (8,9): error CS8079: Use of possibly unassigned auto-implemented property 'P' - // P = ref t; - Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "P").WithArguments("P").WithLocation(8, 9), // (9,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // Q = ref t; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "Q").WithLocation(9, 9), - // (9,9): error CS8079: Use of possibly unassigned auto-implemented property 'Q' - // Q = ref t; - Diagnostic(ErrorCode.ERR_UseDefViolationProperty, "Q").WithArguments("Q").WithLocation(9, 9), // (26,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. // s.P = ref x; Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.P").WithLocation(26, 9), From cf846f7624d34443d39029d288fd180fc98a6522 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Sun, 24 Apr 2022 21:06:25 -0700 Subject: [PATCH 16/29] PR feedback --- src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs | 5 +++-- .../CSharp/Test/Semantic/Semantics/RefFieldTests.cs | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 40e72d0c38bfc..2e6c7a61e397b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -801,7 +801,6 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, { var canModifyReadonly = false; - // PROTOTYPE: Test all of these cases with ref fields (ref, ref readonly, readonly ref, readonly ref readonly) and value kinds (Ref, RefReadOnly, Out). Symbol containing = this.ContainingMemberOrLambda; if ((object)containing != null && fieldIsStatic == containing.IsStatic && @@ -3843,7 +3842,7 @@ private static bool HasHome( return false; } - if (field.RefKind == RefKind.Ref) // PROTOTYPE: What about RefReadOnly? + if (field.RefKind == RefKind.Ref) { return true; } @@ -3860,6 +3859,8 @@ private static bool HasHome( return true; } + Debug.Assert(field.RefKind != RefKind.RefReadOnly); + // Some field accesses must be values; values do not have homes. if (fieldAccess.IsByValue) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 3c4f212f16d7b..9d2d4e69bf851 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -833,6 +833,7 @@ object P }"; // PROTOTYPE: Report a ref-safe-to-escape error for: F = ref tValue; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + // PROTOTYPE: Consider changing ERR_AssignReadonlyNotField to "Cannot take a writable 'ref' to a readonly variable". comp.VerifyEmitDiagnostics( // (10,17): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable // F = ref tIn; // 1 From f82b4750a1cf490a015d58c2402b2f2a6c930072 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 25 Apr 2022 08:43:30 -0700 Subject: [PATCH 17/29] Misc. --- .../Portable/Binder/Binder.ValueChecks.cs | 4 +--- .../Test/Semantic/Semantics/RefFieldTests.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 2e6c7a61e397b..91ce7b3252cef 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -3842,7 +3842,7 @@ private static bool HasHome( return false; } - if (field.RefKind == RefKind.Ref) + if (field.RefKind is RefKind.Ref or RefKind.RefReadOnly) { return true; } @@ -3859,8 +3859,6 @@ private static bool HasHome( return true; } - Debug.Assert(field.RefKind != RefKind.RefReadOnly); - // Some field accesses must be values; values do not have homes. if (fieldAccess.IsByValue) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 9d2d4e69bf851..bbc22351ce207 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -2133,6 +2133,24 @@ static void M4(in T t) { } Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "s.F").WithArguments("field", "S.F").WithLocation(33, 49)); } + [Fact] + public void RefParameter_ReadonlyRefReadonly_PEVerifyCompat() + { + var source = +@"#pragma warning disable 649 +ref struct S +{ + public readonly ref readonly T F; +} +class Program +{ + static void M4(in T t) { } + static void FromValue4B(S s) { M4(s.F); } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithFeature("peverify-compat")); + comp.VerifyEmitDiagnostics(); + } + [Fact] public void InitobjField() { From 345777b4dff0903d79b5392e8c167c1d8c1c71dd Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 25 Apr 2022 15:03:24 -0700 Subject: [PATCH 18/29] PR feedback --- .../CSharp/Portable/CodeGen/EmitExpression.cs | 1 - .../Test/Semantic/Semantics/RefFieldTests.cs | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index c53d052f61483..a90f122dea011 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -2309,7 +2309,6 @@ private static bool TargetIsNotOnHeap(BoundExpression left) return false; } - private bool EmitAssignmentPreamble(BoundAssignmentOperator assignmentOperator) { var assignmentTarget = assignmentOperator.Left; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index bbc22351ce207..070f4bed41cac 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -2142,10 +2142,27 @@ ref struct S { public readonly ref readonly T F; } + class Program { + static void M1(T t) { } static void M4(in T t) { } + + static void FromValue1(S s) { M1(s.F); } + static void FromValue4A(S s) { M4(in s.F); } static void FromValue4B(S s) { M4(s.F); } + + static void FromRef1(ref S s) { M1(s.F); } + static void FromRef4A(ref S s) { M4(in s.F); } + static void FromRef4B(ref S s) { M4(s.F); } + + static void FromOut1(out S s) { s = default; M1(s.F); } + static void FromOut4A(out S s) { s = default; M4(in s.F); } + static void FromOut4B(out S s) { s = default; M4(s.F); } + + static void FromIn1(in S s) { M1(s.F); } + static void FromIn4A(in S s) { M4(in s.F); } + static void FromIn4B(in S s) { M4(s.F); } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithFeature("peverify-compat")); comp.VerifyEmitDiagnostics(); From b7bec5e0025f0d0df2d8c731f1796a5c2f4353e2 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 25 Apr 2022 16:11:54 -0700 Subject: [PATCH 19/29] Update CheckFieldValueKind() --- .../Portable/Binder/Binder.ValueChecks.cs | 5 +- .../CSharp/Portable/CodeGen/EmitExpression.cs | 2 +- .../Test/Semantic/Semantics/RefFieldTests.cs | 121 +++++++----------- 3 files changed, 52 insertions(+), 76 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 91ce7b3252cef..9f38ed6d3c3ac 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -855,6 +855,9 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, if (RequiresRefAssignableVariable(valueKind)) { + Debug.Assert(!fieldIsStatic); + Debug.Assert(valueKind == BindValueKind.RefAssignable); + switch (fieldSymbol.RefKind) { case RefKind.None: @@ -862,7 +865,7 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, return false; case RefKind.Ref: case RefKind.RefReadOnly: - return true; + return CheckIsValidReceiverForVariable(node, fieldAccess.ReceiverOpt, BindValueKind.Assignable, diagnostics); default: throw ExceptionUtilities.UnexpectedValue(fieldSymbol.RefKind); } diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index a90f122dea011..47d1879ae1561 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -2331,7 +2331,7 @@ private bool EmitAssignmentPreamble(BoundAssignmentOperator assignmentOperator) else if (!left.FieldSymbol.IsStatic) { var temp = EmitReceiverRef(left.ReceiverOpt, AddressKind.Writeable); - FreeOptTemp(temp); + Debug.Assert(temp == null, "temp is unexpected when assigning to a field"); lhsUsesStack = true; } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 070f4bed41cac..b651947d50edf 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -546,7 +546,19 @@ static void Main() } }"; var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics(); + comp.VerifyEmitDiagnostics( + // (28,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // new S1().F = ref i; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S1().F").WithLocation(28, 9), + // (29,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // new S2().F = ref i; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S2().F").WithLocation(29, 9), + // (30,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // new S3().F = ref i; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S3().F").WithLocation(30, 9), + // (31,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // new S4().F = ref i; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S4().F").WithLocation(31, 9)); } [Fact] @@ -1163,12 +1175,18 @@ class Program // (22,93): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable // static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 5 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(22, 93), - // (24,61): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. + // (24,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 6 - Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sIn.F = ref tValue").WithArguments("F", "tValue").WithLocation(24, 61), - // (27,73): error CS8331: Cannot assign to variable 'in T' because it is a readonly variable + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(24, 61), + // (25,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(25, 61), + // (26,77): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(26, 77), + // (27,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 7 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "tIn").WithArguments("variable", "in T").WithLocation(27, 73)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61)); } [Fact] @@ -1211,9 +1229,18 @@ class Program // (19,80): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. // static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = ref tValue; } // 2 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sOut.F = ref tValue").WithArguments("F", "tValue").WithLocation(19, 80), - // (24,61): error CS8374: Cannot ref-assign 'tValue' to 'F' because 'tValue' has a narrower escape scope than 'F'. + // (24,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 3 - Diagnostic(ErrorCode.ERR_RefAssignNarrower, "sIn.F = ref tValue").WithArguments("F", "tValue").WithLocation(24, 61)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(24, 61), + // (25,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(25, 61), + // (26,77): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(26, 77), + // (27,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61)); } [Fact] @@ -2341,52 +2368,10 @@ .maxstack 1 } [Fact] - public void ReadWriteFieldWithTemp_01() + public void ReadWriteFieldWithTemp() { var source = -@"using System; -ref struct S -{ - public ref T F; -} -class Program -{ - static void Main() - { - int i = 42; - Console.WriteLine(ReadWrite(ref i)); - } - static T ReadWrite(ref T t) - { - return new S().F = ref t; - } -}"; - var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(@"42")); - verifier.VerifyIL("Program.ReadWrite", -@"{ - // Code size 24 (0x18) - .maxstack 3 - .locals init (S V_0, - T& V_1) - IL_0000: ldloca.s V_0 - IL_0002: dup - IL_0003: initobj ""S"" - IL_0009: ldarg.0 - IL_000a: dup - IL_000b: stloc.1 - IL_000c: stfld ""ref T S.F"" - IL_0011: ldloc.1 - IL_0012: ldobj ""T"" - IL_0017: ret -}"); - } - - [Fact] - public void ReadWriteFieldWithTemp_02() - { - var source = -@"using System; -ref struct S +@"ref struct S { public ref T F; private int _other; @@ -2394,35 +2379,23 @@ ref struct S } class Program { - static void Main() + static T ReadWrite1(ref T t) { - int i = 42; - Console.WriteLine(ReadWrite(ref i)); + return new S().F = ref t; } - static T ReadWrite(ref T t) + static T ReadWrite2(ref T t) { return new S(1).F = ref t; } }"; - var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(@"42")); - verifier.VerifyIL("Program.ReadWrite", -@"{ - // Code size 24 (0x18) - .maxstack 3 - .locals init (S V_0, - T& V_1) - IL_0000: ldc.i4.1 - IL_0001: newobj ""S..ctor(int)"" - IL_0006: stloc.0 - IL_0007: ldloca.s V_0 - IL_0009: ldarg.0 - IL_000a: dup - IL_000b: stloc.1 - IL_000c: stfld ""ref T S.F"" - IL_0011: ldloc.1 - IL_0012: ldobj ""T"" - IL_0017: ret -}"); + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (11,16): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // return new S().F = ref t; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S().F").WithLocation(11, 16), + // (15,16): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // return new S(1).F = ref t; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S(1).F").WithLocation(15, 16)); } [Fact] From dc7e4799ef5a65c9d11533f85556e790c9098d88 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Tue, 26 Apr 2022 11:22:24 -0700 Subject: [PATCH 20/29] Update tests --- .../Test/Semantic/Semantics/RefFieldTests.cs | 61 +++++++++++-------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index b651947d50edf..ad2fdf71969c0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -473,6 +473,10 @@ .locals init (R V_0) }"); } + /// + /// Determination of enum underlying type should ignore fields + /// with custom modifiers or ref custom modifiers. + /// [Fact] public void EnumUnderlyingType() { @@ -536,29 +540,32 @@ public S4(ref T t) } class Program { - static void Main() + static void F(ref T t) { - int i = 0; - new S1().F = ref i; - new S2().F = ref i; - new S3().F = ref i; - new S4().F = ref i; + new S1().F = ref t; + new S2().F = ref t; + new S3().F = ref t; + new S4().F = ref t; + new S1().F = t; + new S2().F = t; + new S3().F = t; + new S4().F = t; } }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( + // (27,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // new S1().F = ref t; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S1().F").WithLocation(27, 9), // (28,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // new S1().F = ref i; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S1().F").WithLocation(28, 9), + // new S2().F = ref t; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S2().F").WithLocation(28, 9), // (29,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // new S2().F = ref i; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S2().F").WithLocation(29, 9), + // new S3().F = ref t; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S3().F").WithLocation(29, 9), // (30,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // new S3().F = ref i; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S3().F").WithLocation(30, 9), - // (31,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // new S4().F = ref i; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S4().F").WithLocation(31, 9)); + // new S4().F = ref t; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S4().F").WithLocation(30, 9)); } [Fact] @@ -1154,9 +1161,9 @@ class Program static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } // 5 static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 6 - static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } - static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } - static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 7 + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 7 + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 8 + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 9 }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( @@ -1179,13 +1186,13 @@ class Program // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 6 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(24, 61), // (25,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable - // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 7 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(25, 61), // (26,77): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable - // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 8 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(26, 77), // (27,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable - // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 7 + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 9 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61)); } @@ -1217,9 +1224,9 @@ class Program static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 3 - static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } - static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } - static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } + static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 4 + static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 5 + static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 6 }"; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( @@ -1233,13 +1240,13 @@ class Program // static void AssignValueToIn(in S sIn, T tValue) { sIn.F = ref tValue; } // 3 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(24, 61), // (25,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable - // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } + // static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = ref tRef; } // 4 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(25, 61), // (26,77): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable - // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } + // static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = ref tOut; } // 5 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(26, 77), // (27,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable - // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } + // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 6 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61)); } From 62ec54b433d8c1e239c4bc57998d342e34578070 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Tue, 26 Apr 2022 14:22:53 -0700 Subject: [PATCH 21/29] Run more tests --- .../Test/Semantic/Semantics/RefFieldTests.cs | 927 +++++++++++++++++- .../Test/Core/CompilationVerifier.cs | 27 + 2 files changed, 940 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index ad2fdf71969c0..5f5e081d2d9c1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -897,7 +897,9 @@ object P public void AssignValueTo_RefField() { var source = -@"ref struct S +@"using System; + +ref struct S { public ref T F; public S(ref T t) { F = ref t; } @@ -915,18 +917,300 @@ class Program static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } - static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } - static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } - static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } - static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } + static void AssignValueToOut(out S sOut, S sInit, T tValue) { sOut = sInit; sOut.F = tValue; } + static void AssignRefToOut(out S sOut, S sInit, ref T tRef) { sOut = sInit; sOut.F = tRef; } + static void AssignOutToOut(out S sOut, S sInit, out T tOut) { sOut = sInit; tOut = default; sOut.F = tOut; } + static void AssignInToOut(out S sOut, S sInit, in T tIn) { sOut = sInit; sOut.F = tIn; } static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } + + static void Main() + { + int x, y; + S s; + + x = 1; y = 2; + s = new S(ref x); + AssignValueToValue(s, y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignRefToValue(s, ref y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignOutToValue(s, out y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignInToValue(s, y); + Console.WriteLine(s.F); + + x = 3; y = 4; + s = new S(ref x); + AssignValueToRef(ref s, y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignRefToRef(ref s, ref y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignOutToRef(ref s, out y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignInToRef(ref s, y); + Console.WriteLine(s.F); + + x = 5; y = 6; + s = new S(ref x); + AssignValueToOut(out s, new S(ref x), y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignRefToOut(out s, new S(ref x), ref y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignOutToOut(out s, new S(ref x), out y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignInToOut(out s, new S(ref x), y); + Console.WriteLine(s.F); + + x = 7; y = 8; + s = new S(ref x); + AssignValueToIn(s, y); + Console.WriteLine(s.F); + x = 7; y = 8; + s = new S(ref x); + AssignRefToIn(s, ref y); + Console.WriteLine(s.F); + x = 7; y = 8; + s = new S(ref x); + AssignOutToIn(s, out y); + Console.WriteLine(s.F); + x = 7; y = 8; + s = new S(ref x); + AssignInToIn(s, y); + Console.WriteLine(s.F); + } }"; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput( +@"2 +2 +0 +2 +4 +4 +0 +4 +6 +6 +0 +6 +8 +8 +0 +8")); + verifier.VerifyILMultiple( + "Program.AssignValueToValue", +@"{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: stobj ""T"" + IL_000c: ret +}", + "Program.AssignRefToValue", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignOutToValue", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.1 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignInToValue", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignValueToRef", +@"{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: stobj ""T"" + IL_000c: ret +}", + "Program.AssignRefToRef", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignOutToRef", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.1 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignInToRef", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignValueToOut", +@"{ + // Code size 20 (0x14) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.2 + IL_000e: stobj ""T"" + IL_0013: ret +}", + "Program.AssignRefToOut", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.2 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignOutToOut", +@"{ + // Code size 32 (0x20) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stobj ""S"" + IL_0007: ldarg.2 + IL_0008: initobj ""T"" + IL_000e: ldarg.0 + IL_000f: ldfld ""ref T S.F"" + IL_0014: ldarg.2 + IL_0015: ldobj ""T"" + IL_001a: stobj ""T"" + IL_001f: ret +}", + "Program.AssignInToOut", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.2 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignValueToIn", +@"{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: stobj ""T"" + IL_000c: ret +}", + "Program.AssignRefToIn", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignOutToIn", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.1 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignInToIn", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}"); } [Fact] @@ -1017,7 +1301,9 @@ class Program public void AssignValueTo_ReadonlyRefField() { var source = -@"ref struct S +@"using System; + +ref struct S { public readonly ref T F; public S(ref T t) { F = ref t; } @@ -1035,18 +1321,300 @@ class Program static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = tOut; } static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = tIn; } - static void AssignValueToOut(out S sOut, T tValue) { sOut = default; sOut.F = tValue; } - static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = tRef; } - static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = tOut; } - static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = tIn; } + static void AssignValueToOut(out S sOut, S sInit, T tValue) { sOut = sInit; sOut.F = tValue; } + static void AssignRefToOut(out S sOut, S sInit, ref T tRef) { sOut = sInit; sOut.F = tRef; } + static void AssignOutToOut(out S sOut, S sInit, out T tOut) { sOut = sInit; tOut = default; sOut.F = tOut; } + static void AssignInToOut(out S sOut, S sInit, in T tIn) { sOut = sInit; sOut.F = tIn; } static void AssignValueToIn(in S sIn, T tValue) { sIn.F = tValue; } static void AssignRefToIn(in S sIn, ref T tRef) { sIn.F = tRef; } static void AssignOutToIn(in S sIn, out T tOut) { tOut = default; sIn.F = tOut; } static void AssignInToIn(in S sIn, in T tIn) { sIn.F = tIn; } + + static void Main() + { + int x, y; + S s; + + x = 1; y = 2; + s = new S(ref x); + AssignValueToValue(s, y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignRefToValue(s, ref y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignOutToValue(s, out y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignInToValue(s, y); + Console.WriteLine(s.F); + + x = 3; y = 4; + s = new S(ref x); + AssignValueToRef(ref s, y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignRefToRef(ref s, ref y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignOutToRef(ref s, out y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignInToRef(ref s, y); + Console.WriteLine(s.F); + + x = 5; y = 6; + s = new S(ref x); + AssignValueToOut(out s, new S(ref x), y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignRefToOut(out s, new S(ref x), ref y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignOutToOut(out s, new S(ref x), out y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignInToOut(out s, new S(ref x), y); + Console.WriteLine(s.F); + + x = 7; y = 8; + s = new S(ref x); + AssignValueToIn(s, y); + Console.WriteLine(s.F); + x = 7; y = 8; + s = new S(ref x); + AssignRefToIn(s, ref y); + Console.WriteLine(s.F); + x = 7; y = 8; + s = new S(ref x); + AssignOutToIn(s, out y); + Console.WriteLine(s.F); + x = 7; y = 8; + s = new S(ref x); + AssignInToIn(s, y); + Console.WriteLine(s.F); + } }"; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput( +@"2 +2 +0 +2 +4 +4 +0 +4 +6 +6 +0 +6 +8 +8 +0 +8")); + verifier.VerifyILMultiple( + "Program.AssignValueToValue", +@"{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: stobj ""T"" + IL_000c: ret +}", + "Program.AssignRefToValue", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignOutToValue", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.1 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignInToValue", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignValueToRef", +@"{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: stobj ""T"" + IL_000c: ret +}", + "Program.AssignRefToRef", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignOutToRef", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.1 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignInToRef", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignValueToOut", +@"{ + // Code size 20 (0x14) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.2 + IL_000e: stobj ""T"" + IL_0013: ret +}", + "Program.AssignRefToOut", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.2 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignOutToOut", +@"{ + // Code size 32 (0x20) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stobj ""S"" + IL_0007: ldarg.2 + IL_0008: initobj ""T"" + IL_000e: ldarg.0 + IL_000f: ldfld ""ref T S.F"" + IL_0014: ldarg.2 + IL_0015: ldobj ""T"" + IL_001a: stobj ""T"" + IL_001f: ret +}", + "Program.AssignInToOut", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.2 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignValueToIn", +@"{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: stobj ""T"" + IL_000c: ret +}", + "Program.AssignRefToIn", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}", + "Program.AssignOutToIn", +@"{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarg.0 + IL_0008: ldfld ""ref T S.F"" + IL_000d: ldarg.1 + IL_000e: ldobj ""T"" + IL_0013: stobj ""T"" + IL_0018: ret +}", + "Program.AssignInToIn", +@"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldfld ""ref T S.F"" + IL_0006: ldarg.1 + IL_0007: ldobj ""T"" + IL_000c: stobj ""T"" + IL_0011: ret +}"); } [Fact] @@ -1194,6 +1762,148 @@ class Program // (27,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 9 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61)); + + // Valid cases from above. + source = +@"using System; + +ref struct S +{ + public ref T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } + static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } + + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } + + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } + + static void Main() + { + int x, y; + S s; + + x = 1; y = 2; + s = new S(ref x); + AssignValueToValue(s, y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignRefToValue(s, ref y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignOutToValue(s, out y); + Console.WriteLine(s.F); + + x = 3; y = 4; + s = new S(ref x); + AssignRefToRef(ref s, ref y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignOutToRef(ref s, out y); + Console.WriteLine(s.F); + + x = 5; y = 6; + s = new S(ref x); + AssignRefToOut(out s, ref y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignOutToOut(out s, out y); + Console.WriteLine(s.F); + } +}"; + var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput( +@"1 +1 +1 +4 +0 +6 +0")); + verifier.VerifyILMultiple( + "Program.AssignValueToValue", +@"{ + // Code size 10 (0xa) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldarga.s V_1 + IL_0004: stfld ""ref T S.F"" + IL_0009: ret +}", + "Program.AssignRefToValue", +@"{ + // Code size 9 (0x9) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldarg.1 + IL_0003: stfld ""ref T S.F"" + IL_0008: ret +}", + "Program.AssignOutToValue", +@"{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarga.s V_0 + IL_0009: ldarg.1 + IL_000a: stfld ""ref T S.F"" + IL_000f: ret +}", + "Program.AssignRefToRef", +@"{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld ""ref T S.F"" + IL_0007: ret +}", + "Program.AssignOutToRef", +@"{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarg.0 + IL_0008: ldarg.1 + IL_0009: stfld ""ref T S.F"" + IL_000e: ret +}", + "Program.AssignRefToOut", +@"{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: initobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldarg.1 + IL_0009: stfld ""ref T S.F"" + IL_000e: ret +}", + "Program.AssignOutToOut", +@"{ + // Code size 22 (0x16) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: initobj ""S"" + IL_0007: ldarg.1 + IL_0008: initobj ""T"" + IL_000e: ldarg.0 + IL_000f: ldarg.1 + IL_0010: stfld ""ref T S.F"" + IL_0015: ret +}"); } [Fact] @@ -1248,6 +1958,195 @@ class Program // (27,61): error CS8332: Cannot assign to a member of variable 'in S' because it is a readonly variable // static void AssignInToIn(in S sIn, in T tIn) { sIn.F = ref tIn; } // 6 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "sIn.F").WithArguments("variable", "in S").WithLocation(27, 61)); + + // Valid cases from above. + source = +@"using System; + +ref struct S +{ + public ref readonly T F; + public S(ref T t) { F = ref t; } +} + +class Program +{ + static void AssignValueToValue(S s, T tValue) { s.F = ref tValue; } + static void AssignRefToValue(S s, ref T tRef) { s.F = ref tRef; } + static void AssignOutToValue(S s, out T tOut) { tOut = default; s.F = ref tOut; } + static void AssignInToValue(S s, in T tIn) { s.F = ref tIn; } + + static void AssignRefToRef(ref S sRef, ref T tRef) { sRef.F = ref tRef; } + static void AssignOutToRef(ref S sRef, out T tOut) { tOut = default; sRef.F = ref tOut; } + static void AssignInToRef(ref S sRef, in T tIn) { sRef.F = ref tIn; } + + static void AssignRefToOut(out S sOut, ref T tRef) { sOut = default; sOut.F = ref tRef; } + static void AssignOutToOut(out S sOut, out T tOut) { sOut = default; tOut = default; sOut.F = ref tOut; } + static void AssignInToOut(out S sOut, in T tIn) { sOut = default; sOut.F = ref tIn; } + + static void Main() + { + int x, y; + S s; + + x = 1; y = 2; + s = new S(ref x); + AssignValueToValue(s, y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignRefToValue(s, ref y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignOutToValue(s, out y); + Console.WriteLine(s.F); + x = 1; y = 2; + s = new S(ref x); + AssignInToValue(s, y); + Console.WriteLine(s.F); + + x = 3; y = 4; + s = new S(ref x); + AssignRefToRef(ref s, ref y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignOutToRef(ref s, out y); + Console.WriteLine(s.F); + x = 3; y = 4; + s = new S(ref x); + AssignInToRef(ref s, y); + Console.WriteLine(s.F); + + x = 5; y = 6; + s = new S(ref x); + AssignRefToOut(out s, ref y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignOutToOut(out s, out y); + Console.WriteLine(s.F); + x = 5; y = 6; + s = new S(ref x); + AssignInToOut(out s, y); + Console.WriteLine(s.F); + } +}"; + var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput( +@"1 +1 +1 +1 +4 +0 +4 +6 +0 +6")); + verifier.VerifyILMultiple( + "Program.AssignValueToValue", +@"{ + // Code size 10 (0xa) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldarga.s V_1 + IL_0004: stfld ""ref readonly T S.F"" + IL_0009: ret +}", + "Program.AssignRefToValue", +@"{ + // Code size 9 (0x9) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldarg.1 + IL_0003: stfld ""ref readonly T S.F"" + IL_0008: ret +}", + "Program.AssignOutToValue", +@"{ + // Code size 16 (0x10) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarga.s V_0 + IL_0009: ldarg.1 + IL_000a: stfld ""ref readonly T S.F"" + IL_000f: ret +}", + "Program.AssignInToValue", +@"{ + // Code size 9 (0x9) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldarg.1 + IL_0003: stfld ""ref readonly T S.F"" + IL_0008: ret +}", + "Program.AssignRefToRef", +@"{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld ""ref readonly T S.F"" + IL_0007: ret +}", + "Program.AssignOutToRef", +@"{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: initobj ""T"" + IL_0007: ldarg.0 + IL_0008: ldarg.1 + IL_0009: stfld ""ref readonly T S.F"" + IL_000e: ret +}", + "Program.AssignInToRef", +@"{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld ""ref readonly T S.F"" + IL_0007: ret +}", + "Program.AssignRefToOut", +@"{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: initobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldarg.1 + IL_0009: stfld ""ref readonly T S.F"" + IL_000e: ret +}", + "Program.AssignOutToOut", +@"{ + // Code size 22 (0x16) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: initobj ""S"" + IL_0007: ldarg.1 + IL_0008: initobj ""T"" + IL_000e: ldarg.0 + IL_000f: ldarg.1 + IL_0010: stfld ""ref readonly T S.F"" + IL_0015: ret +}", + "Program.AssignInToOut", +@"{ + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: initobj ""S"" + IL_0007: ldarg.0 + IL_0008: ldarg.1 + IL_0009: stfld ""ref readonly T S.F"" + IL_000e: ret +}"); } [Fact] diff --git a/src/Compilers/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index c76f113040c23..1ad31466a0ee6 100644 --- a/src/Compilers/Test/Core/CompilationVerifier.cs +++ b/src/Compilers/Test/Core/CompilationVerifier.cs @@ -13,6 +13,7 @@ using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; +using System.Text; using System.Xml.Linq; using ICSharpCode.Decompiler.Metadata; using Microsoft.CodeAnalysis; @@ -437,6 +438,32 @@ public CompilationVerifier VerifyIL( return VerifyILImpl(qualifiedMethodName, expectedIL, realIL, sequencePoints, callerPath, callerLine, escapeQuotes: true, source: source); } + public void VerifyILMultiple(params string[] qualifiedMethodNamesAndExpectedIL) + { + var names = ArrayBuilder.GetInstance(); + var expected = ArrayBuilder.GetInstance(); + var actual = ArrayBuilder.GetInstance(); + for (int i = 0; i < qualifiedMethodNamesAndExpectedIL.Length; ) + { + var qualifiedName = qualifiedMethodNamesAndExpectedIL[i++]; + names.Add(qualifiedName); + actual.Add(AssertEx.NormalizeWhitespace(VisualizeIL(qualifiedName))); + expected.Add(AssertEx.NormalizeWhitespace(qualifiedMethodNamesAndExpectedIL[i++])); + } + if (!expected.SequenceEqual(actual)) + { + var builder = new StringBuilder(); + for (int i = 0; i < expected.Count; i++) + { + builder.AppendLine(AssertEx.GetAssertMessage(expected[i], actual[i], prefix: names[i], escapeQuotes: true)); + } + Assert.True(false, builder.ToString()); + } + actual.Free(); + expected.Free(); + names.Free(); + } + public CompilationVerifier VerifyMissing( string qualifiedMethodName) { From 8cff50f7f0668ba6be450d9304211ce939f40497 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 27 Apr 2022 02:21:55 -0700 Subject: [PATCH 22/29] Fix tests --- .../CSharp/Test/Semantic/Semantics/RefFieldTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 5f5e081d2d9c1..332cfc4aac1a0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -1001,7 +1001,7 @@ static void Main() Console.WriteLine(s.F); } }"; - var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput( + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 0 @@ -1405,7 +1405,7 @@ static void Main() Console.WriteLine(s.F); } }"; - var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput( + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 2 0 @@ -1822,7 +1822,7 @@ static void Main() Console.WriteLine(s.F); } }"; - var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput( + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"1 1 1 @@ -2033,7 +2033,7 @@ static void Main() Console.WriteLine(s.F); } }"; - var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput( + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"1 1 1 From 6bfd88cf3abdb06787861d9329c0a3f4bdc9e21e Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 27 Apr 2022 02:43:20 -0700 Subject: [PATCH 23/29] Misc. --- .../Test/Semantic/Semantics/RefFieldTests.cs | 17 ++++++++++++++--- src/Compilers/Test/Core/CompilationVerifier.cs | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 332cfc4aac1a0..ab20d426e9cb7 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -3318,10 +3318,11 @@ class Program static void Main() { int i = 1; - ReadAndDiscard(ref i); + ReadAndDiscard1(ref i); ReadAndDiscardNoArg(); + ReadAndDiscard2(new S(ref i)); } - static void ReadAndDiscard(ref T t) + static void ReadAndDiscard1(ref T t) { _ = new S(ref t).F; } @@ -3329,11 +3330,15 @@ static void ReadAndDiscardNoArg() { _ = new S().F; } + static void ReadAndDiscard2(in S s) + { + _ = s.F; + } }"; // PROTOTYPE: The dereference of `new S(...).F` should not be elided // since the behavior may be observable as a NullReferenceException. var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("")); - verifier.VerifyIL("Program.ReadAndDiscard", + verifier.VerifyIL("Program.ReadAndDiscard1", @"{ // Code size 8 (0x8) .maxstack 1 @@ -3347,6 +3352,12 @@ .maxstack 1 // Code size 1 (0x1) .maxstack 0 IL_0000: ret +}"); + verifier.VerifyIL("Program.ReadAndDiscard2", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret }"); } diff --git a/src/Compilers/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index 1ad31466a0ee6..d22002f2ab12d 100644 --- a/src/Compilers/Test/Core/CompilationVerifier.cs +++ b/src/Compilers/Test/Core/CompilationVerifier.cs @@ -443,7 +443,7 @@ public void VerifyILMultiple(params string[] qualifiedMethodNamesAndExpectedIL) var names = ArrayBuilder.GetInstance(); var expected = ArrayBuilder.GetInstance(); var actual = ArrayBuilder.GetInstance(); - for (int i = 0; i < qualifiedMethodNamesAndExpectedIL.Length; ) + for (int i = 0; i < qualifiedMethodNamesAndExpectedIL.Length;) { var qualifiedName = qualifiedMethodNamesAndExpectedIL[i++]; names.Add(qualifiedName); From 8e51523e6311f386f4af0fc6631e2b04d0231aca Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 27 Apr 2022 15:38:11 -0700 Subject: [PATCH 24/29] PR feedback --- .../CSharp/Portable/Symbols/FieldSymbol.cs | 1 + .../Metadata/PE/MemberRefMetadataDecoder.cs | 4 +-- .../Test/Semantic/Semantics/RefFieldTests.cs | 11 +++++++ .../CodeGen/ReferenceDependencyWalker.cs | 32 +++++++++---------- .../Core/Portable/PEWriter/MetadataVisitor.cs | 1 + .../Portable/PEWriter/ReferenceIndexerBase.cs | 1 + 6 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs index 9df43536b764b..216f65aea252c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs @@ -348,6 +348,7 @@ internal bool CalculateUseSiteDiagnostic(ref UseSiteInfo result) Debug.Assert(IsDefinition); // Check type, custom modifiers + // PROTOTYPE: Disallow volatile for ref fields. if (DeriveUseSiteInfoFromType(ref result, this.TypeWithAnnotations, AllowedRequiredModifierType.System_Runtime_CompilerServices_Volatile)) { return true; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs index 55f12302bde1a..0c877aa3df65c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs @@ -159,9 +159,7 @@ private static FieldSymbol FindFieldBySignature(TypeSymbol targetTypeSymbol, str var field = member as FieldSymbol; TypeWithAnnotations fieldType; - // Ensure the field symbol matches the { IsByRef, RefCustomModifiers, Type, CustomModifiers } - // from metadata. The checks are not strictly necessary though since PENamedTypeSymbol - // does not support fields with duplicate names. Matching by name is sufficient. + // Ensure the field symbol matches the { IsByRef, RefCustomModifiers, Type, CustomModifiers } from metadata. if ((object)field != null && (field.RefKind != RefKind.None) == fieldInfo.IsByRef && CustomModifiersMatch(field.RefCustomModifiers, fieldInfo.RefCustomModifiers) && diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index ab20d426e9cb7..3ec02a2a9760f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -444,6 +444,8 @@ public void MemberRefMetadataDecoder_FindFieldBySignature() @".class public sealed R extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .field private int32& F + .field private !0& modopt(int32) F .field public !0& modopt(object) F }"; var refA = CompileIL(sourceA); @@ -471,6 +473,15 @@ .locals init (R V_0) IL_000e: ldind.i4 IL_000f: ret }"); + + // Call MemberRefMetadataDecoder.FindFieldBySignature() indirectly from MetadataDecoder.GetSymbolForILToken(). + var module = (PEModuleSymbol)((CSharpCompilation)verifier.Compilation).GetReferencedAssemblySymbol(refA).Modules[0]; + var decoder = new MetadataDecoder(module); + var reader = module.Module.MetadataReader; + var fields = reader.FieldDefinitions. + Where(handle => reader.GetString(reader.GetFieldDefinition(handle).Name) == "F"). + Select(handle => decoder.GetSymbolForILToken(handle)).ToArray(); + AssertEx.Equal(new[] { "ref System.Int32 R.F", "ref modopt(System.Int32) T R.F", "ref modopt(System.Object) T R.F" }, fields.ToTestDisplayStrings()); } /// diff --git a/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs b/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs index 0bf8aa2841a21..d0937bc9eba9a 100644 --- a/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs +++ b/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs @@ -70,10 +70,7 @@ private static void VisitTypeReference(Cci.ITypeReference typeReference, EmitCon Cci.IModifiedTypeReference? modifiedType = typeReference as Cci.IModifiedTypeReference; if (modifiedType != null) { - foreach (var custModifier in modifiedType.CustomModifiers) - { - VisitTypeReference(custModifier.GetModifier(context), context); - } + VisitCustomModifiers(modifiedType.CustomModifiers, context); VisitTypeReference(modifiedType.UnmodifiedType, context); return; } @@ -142,10 +139,7 @@ internal static void VisitSignature(Cci.ISignature signature, EmitContext contex // Visit return value type VisitTypeReference(signature.GetType(context), context); - foreach (var typeModifier in signature.RefCustomModifiers) - { - VisitTypeReference(typeModifier.GetModifier(context), context); - } + VisitCustomModifiers(signature.RefCustomModifiers, context); foreach (var typeModifier in signature.ReturnValueCustomModifiers) { @@ -159,15 +153,9 @@ private static void VisitParameters(ImmutableArray customModifiers, in EmitContext context) + { + foreach (var typeModifier in customModifiers) + { + VisitTypeReference(typeModifier.GetModifier(context), context); + } } } } diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs index 6068d257f6cfe..f5e4d1a32018e 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs @@ -123,6 +123,7 @@ public virtual void Visit(IFieldDefinition fieldDefinition) public virtual void Visit(IFieldReference fieldReference) { + this.Visit(fieldReference.RefCustomModifiers); this.Visit((ITypeMemberReference)fieldReference); } diff --git a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs index e0f5e82919cc9..328f5f6f279f2 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs @@ -60,6 +60,7 @@ public override void Visit(IFieldReference fieldReference) return; } + this.Visit(fieldReference.RefCustomModifiers); this.Visit((ITypeMemberReference)fieldReference); this.Visit(fieldReference.GetType(Context)); ReserveFieldToken(fieldReference); From 7c8b1e356fd0a44724803a57c785bc162a7b42ca Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:01:48 -0700 Subject: [PATCH 25/29] Misc. --- .../Core/Portable/CodeGen/ReferenceDependencyWalker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs b/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs index d0937bc9eba9a..1e2a999910fbd 100644 --- a/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs +++ b/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs @@ -163,8 +163,6 @@ private static void VisitFieldReference(Cci.IFieldReference fieldReference, Emit { RoslynDebug.Assert(fieldReference != null); - //System.Diagnostics.Debug.Assert(fieldReference.RefCustomModifiers.IsEmpty); - // Visit containing type VisitTypeReference(fieldReference.GetContainingType(context), context); From 2f134c44e88881041f30dcfe60172f118addd2ec Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 27 Apr 2022 21:23:04 -0700 Subject: [PATCH 26/29] PR feedback --- .../Symbols/Metadata/PE/PEFieldSymbol.cs | 1 + .../Symbols/Metadata/PE/PENamedTypeSymbol.cs | 2 +- .../Test/Semantic/Semantics/RefFieldTests.cs | 98 ++++++---- .../Syntax/Parsing/RefFieldParsingTests.cs | 177 ++++++++++++++++++ .../Core/Portable/PEWriter/MetadataVisitor.cs | 1 - 5 files changed, 242 insertions(+), 37 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs index 9c5b4d7d74b34..702c62b020bb9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs @@ -304,6 +304,7 @@ private void EnsureSignatureIsLoaded() _packedFlags.SetRefKind(refKind); _packedFlags.SetIsVolatile(customModifiersArray.Any(m => !m.IsOptional && ((CSharpCustomModifier)m).ModifierSymbol.SpecialType == SpecialType.System_Runtime_CompilerServices_IsVolatile)); + // PROTOTYPE: `fixed ref` field use should be disallowed. TypeSymbol fixedElementType; int fixedSize; if (customModifiersArray.IsEmpty && IsFixedBuffer(out fixedSize, out fixedElementType)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index dae9eb3b321b7..cfd472f4618d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -1155,7 +1155,7 @@ private void EnsureEnumUnderlyingTypeIsLoaded(UncommonProperties uncommon) TypeSymbol type = fieldInfo.Type; if (type.SpecialType.IsValidEnumUnderlyingType() && - !fieldInfo.RefCustomModifiers.AnyRequired() && + !fieldInfo.IsByRef && !fieldInfo.CustomModifiers.AnyRequired()) { if ((object)underlyingType == null) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 3ec02a2a9760f..133b87842955b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -155,21 +155,14 @@ static void M3(S s) // M2(ref s.F1); Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F1").WithArguments("ref fields").WithLocation(22, 16)); - verifyField(comp.GetMember("S.F1"), RefKind.Ref, "ref T S.F1"); - verifyField(comp.GetMember("S.F2"), RefKind.RefReadOnly, "ref readonly T S.F2"); + VerifyFieldSymbol(comp.GetMember("S.F1"), "ref T S.F1", RefKind.Ref, new string[0]); + VerifyFieldSymbol(comp.GetMember("S.F2"), "ref readonly T S.F2", RefKind.RefReadOnly, new string[0]); comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext); comp.VerifyEmitDiagnostics(); - verifyField(comp.GetMember("S.F1"), RefKind.Ref, "ref T S.F1"); - verifyField(comp.GetMember("S.F2"), RefKind.RefReadOnly, "ref readonly T S.F2"); - - static void verifyField(FieldSymbol field, RefKind refKind, string displayName) - { - Assert.Equal(refKind, field.RefKind); - Assert.Equal(displayName, field.ToTestDisplayString()); - Assert.Null(field.GetUseSiteDiagnostic()); - } + VerifyFieldSymbol(comp.GetMember("S.F1"), "ref T S.F1", RefKind.Ref, new string[0]); + VerifyFieldSymbol(comp.GetMember("S.F2"), "ref readonly T S.F2", RefKind.RefReadOnly, new string[0]); } [CombinatorialData] @@ -188,12 +181,12 @@ public void RefField(bool useCompilationReference) // public ref T F; Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref T").WithArguments("ref fields").WithLocation(3, 12)); - verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); + VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); comp = CreateCompilation(sourceA); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); - verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); + VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); var sourceB = @"using System; @@ -224,7 +217,7 @@ static void Main() // Console.WriteLine(s.F); Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F").WithArguments("ref fields").WithLocation(12, 27)); - verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); + VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); var verifier = CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 @@ -233,13 +226,7 @@ static void Main() 3 ")); comp = (CSharpCompilation)verifier.Compilation; - verifyField(comp.GetMember("S.F"), RefKind.Ref, "ref T S.F"); - - static void verifyField(FieldSymbol field, RefKind refKind, string displayName) - { - Assert.Equal(refKind, field.RefKind); - Assert.Equal(displayName, field.ToTestDisplayString()); - } + VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); } [CombinatorialData] @@ -261,12 +248,12 @@ public S(in T t) // public ref readonly T F; Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref readonly T").WithArguments("ref fields").WithLocation(3, 12)); - verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); + VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); comp = CreateCompilation(sourceA); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); - verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); + VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); var sourceB = @"using System; @@ -302,7 +289,7 @@ static void Main() // Console.WriteLine(s.F.G); Diagnostic(ErrorCode.ERR_FeatureInPreview, "s.F").WithArguments("ref fields").WithLocation(17, 27)); - verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); + VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); var verifier = CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularNext, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( @"2 @@ -311,13 +298,7 @@ static void Main() 3 ")); comp = (CSharpCompilation)verifier.Compilation; - verifyField(comp.GetMember("S.F"), RefKind.RefReadOnly, "ref readonly T S.F"); - - static void verifyField(FieldSymbol field, RefKind refKind, string displayName) - { - Assert.Equal(refKind, field.RefKind); - Assert.Equal(displayName, field.ToTestDisplayString()); - } + VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); } [Fact] @@ -437,6 +418,52 @@ static void F(object o) Assert.Equal(new[] { "System.Int32", "System.Object" }, fieldInfo.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); } + [Fact] + public void FixedField_01() + { + var source = +@"unsafe ref struct S +{ + public fixed ref int F[3]; +}"; + // PROTOTYPE: `fixed ref` field declaration should be disallowed. + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void FixedField_02() + { + var sourceA = +@".class public sealed S extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .class sequential nested public sealed FixedBuffer extends [mscorlib]System.ValueType + { + .pack 0 + .size 12 + .field public int32 FixedElementField + } + .field public valuetype S/FixedBuffer& F + .custom instance void [mscorlib]System.Runtime.CompilerServices.FixedBufferAttribute::.ctor(class [mscorlib]System.Type, int32) = { type([mscorlib]System.Int32) int32(3) } +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"using System; +class Program +{ + unsafe static void Main() + { + var s = new S(); + Console.WriteLine(s.F[1]); + } +}"; + // PROTOTYPE: `fixed ref` field use should be disallowed. + var comp = CreateCompilation(sourceB, references: new[] { refA }, options: TestOptions.UnsafeReleaseExe); + comp.VerifyEmitDiagnostics(); + } + [Fact] public void MemberRefMetadataDecoder_FindFieldBySignature() { @@ -485,8 +512,8 @@ .locals init (R V_0) } /// - /// Determination of enum underlying type should ignore fields - /// with custom modifiers or ref custom modifiers. + /// Determination of enum underlying type should ignore ref fields + /// and fields with required custom modifiers. /// [Fact] public void EnumUnderlyingType() @@ -495,8 +522,9 @@ public void EnumUnderlyingType() @".class public sealed E extends [mscorlib]System.Enum { .field public int64 modreq(object) value1 - .field public int32& modreq(object) value2 - .field public int16 value3 + .field public int32& modopt(object) value2 + .field public int32& value3 + .field public int16 value4 .field public static literal valuetype E A = int16(0x01) }"; var refA = CompileIL(sourceA); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs index 71b3f8bc541a3..f8a4fc8feda4c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs @@ -161,6 +161,183 @@ public void FieldDeclaration_04(LanguageVersion languageVersion) EOF(); } + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void Fixed_01(LanguageVersion languageVersion) + { + string source = "struct S { fixed ref int F[1]; }"; + UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion)); + + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "S"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void Fixed_02(LanguageVersion languageVersion) + { + string source = "struct S { ref fixed int F[1]; }"; + UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,16): error CS1031: Type expected + // struct S { ref fixed int F[1]; } + Diagnostic(ErrorCode.ERR_TypeExpected, "fixed").WithLocation(1, 16)); + + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "S"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void ReadOnlyRefParameter(LanguageVersion languageVersion) + { + string source = "class C { void M(readonly ref int i) { } }"; + UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,1): error CS1073: Unexpected token '}' + // class C { void M(readonly ref int i) { } } + Diagnostic(ErrorCode.ERR_UnexpectedToken, "class C { void M(readonly ref int i) { }").WithArguments("}").WithLocation(1, 1), + // (1,18): error CS1026: ) expected + // class C { void M(readonly ref int i) { } } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(1, 18), + // (1,18): error CS1002: ; expected + // class C { void M(readonly ref int i) { } } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(1, 18), + // (1,36): error CS1003: Syntax error, ',' expected + // class C { void M(readonly ref int i) { } } + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",", ")").WithLocation(1, 36), + // (1,40): error CS1002: ; expected + // class C { void M(readonly ref int i) { } } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(1, 40)); + + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + // PROTOTYPE: Should ref expressions be supported in object initializers? [Theory] [InlineData(LanguageVersion.CSharp10)] diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs index f5e4d1a32018e..6068d257f6cfe 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs @@ -123,7 +123,6 @@ public virtual void Visit(IFieldDefinition fieldDefinition) public virtual void Visit(IFieldReference fieldReference) { - this.Visit(fieldReference.RefCustomModifiers); this.Visit((ITypeMemberReference)fieldReference); } From a49068d098d2dc00bd1f98793c15923f5af42deb Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 28 Apr 2022 11:22:28 -0700 Subject: [PATCH 27/29] PR feedback --- .../Portable/Binder/Binder.ValueChecks.cs | 5 +- .../CSharp/Portable/Symbols/FieldSymbol.cs | 3 +- .../Test/Semantic/Semantics/RefFieldTests.cs | 172 ++++++++++++++++-- .../Syntax/Parsing/RefFieldParsingTests.cs | 90 ++++++++- .../Test/Utilities/VisualBasic/Extensions.vb | 5 - .../Metadata/PE/MemberRefMetadataDecoder.vb | 3 + .../Test/Semantic/Semantics/RefFieldTests.vb | 128 ++++++++++++- 7 files changed, 365 insertions(+), 41 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 9f38ed6d3c3ac..0cbff6bdd5413 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -796,8 +796,7 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, // S has a mutable field x, then c.f.x is not a variable because c.f is not // writable. - if ((RequiresAssignableVariable(valueKind) && fieldSymbol.RefKind == RefKind.None) || - RequiresRefAssignableVariable(valueKind)) + if (fieldSymbol.RefKind == RefKind.None ? RequiresAssignableVariable(valueKind) : RequiresRefAssignableVariable(valueKind)) { var canModifyReadonly = false; @@ -1919,7 +1918,7 @@ private static ErrorCode GetStandardRValueRefEscapeError(uint escapeTo) private static void ReportReadOnlyFieldError(FieldSymbol field, SyntaxNode node, BindValueKind kind, bool checkingReceiver, BindingDiagnosticBag diagnostics) { Debug.Assert((object)field != null); - Debug.Assert(RequiresAssignableVariable(kind) && field.RefKind == RefKind.None || RequiresRefAssignableVariable(kind)); + Debug.Assert(field.RefKind == RefKind.None ? RequiresAssignableVariable(kind) : RequiresRefAssignableVariable(kind)); Debug.Assert(field.Type != (object)null); // It's clearer to say that the address can't be taken than to say that the field can't be modified diff --git a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs index 216f65aea252c..4970ae56b4e6d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs @@ -349,7 +349,8 @@ internal bool CalculateUseSiteDiagnostic(ref UseSiteInfo result) // Check type, custom modifiers // PROTOTYPE: Disallow volatile for ref fields. - if (DeriveUseSiteInfoFromType(ref result, this.TypeWithAnnotations, AllowedRequiredModifierType.System_Runtime_CompilerServices_Volatile)) + if (DeriveUseSiteInfoFromType(ref result, this.TypeWithAnnotations, AllowedRequiredModifierType.System_Runtime_CompilerServices_Volatile) || + DeriveUseSiteInfoFromCustomModifiers(ref result, this.RefCustomModifiers, AllowedRequiredModifierType.None)) { return true; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 133b87842955b..f36e8ba43533f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -424,7 +424,8 @@ public void FixedField_01() var source = @"unsafe ref struct S { - public fixed ref int F[3]; + public fixed ref int F1[3]; + public fixed ref readonly int F2[3]; }"; // PROTOTYPE: `fixed ref` field declaration should be disallowed. var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll); @@ -464,51 +465,182 @@ unsafe static void Main() comp.VerifyEmitDiagnostics(); } + /// + /// Unexpected modreq(). + /// [Fact] - public void MemberRefMetadataDecoder_FindFieldBySignature() + public void RefCustomModifiers_UseSiteDiagnostic_02() { var sourceA = -@".class public sealed R extends [mscorlib]System.ValueType +@".class public sealed A extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) - .field private int32& F - .field private !0& modopt(int32) F - .field public !0& modopt(object) F + .field public int32& modreq(int32) F }"; var refA = CompileIL(sourceA); var sourceB = -@"class Program +@"using System; +class Program { - static int Main() + static void Main() { - var r = new R(); - return r.F; + var a = new A(); + Console.WriteLine(a.F); } +}"; + var comp = CreateCompilation(sourceB, new[] { refA }); + comp.VerifyEmitDiagnostics( + // (7,29): error CS0570: 'A.F' is not supported by the language + // Console.WriteLine(a.F); + Diagnostic(ErrorCode.ERR_BindToBogus, "F").WithArguments("A.F").WithLocation(7, 29)); + } + + /// + /// modreq(System.Runtime.InteropServices.InAttribute). + /// Should we allow this modreq() even though it is not generated by the compiler? + /// + [Fact] + public void RefCustomModifiers_UseSiteDiagnostic_01() + { + var sourceA = +@".class public sealed A extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .field public int32& modreq([mscorlib]System.Runtime.InteropServices.InAttribute) F +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"using System; +class Program +{ + static void Main() + { + var a = new A(); + Console.WriteLine(a.F); + } +}"; + var comp = CreateCompilation(sourceB, new[] { refA }); + comp.VerifyEmitDiagnostics( + // (7,29): error CS0570: 'A.F' is not supported by the language + // Console.WriteLine(a.F); + Diagnostic(ErrorCode.ERR_BindToBogus, "F").WithArguments("A.F").WithLocation(7, 29)); + } + + /// + /// modopt() with missing type. + /// + [Fact] + public void RefCustomModifiers_UseSiteDiagnostic_03() + { + var sourceA = +@".assembly extern mscorlib { } +.assembly A { } +.class public A +{ +}"; + var refA = CompileIL(sourceA, prependDefaultHeader: false); + + var sourceB = +@".assembly extern A { } +.class public sealed B extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .field public int32& modopt(int8) modopt([A]A) F +}"; + var refB = CompileIL(sourceB); + + var sourceC = +@"using System; +class Program +{ + static void Main() + { + var b = new B(); + Console.WriteLine(b.F); + } +}"; + var comp = CreateCompilation(sourceC, new[] { refB }); + comp.VerifyEmitDiagnostics( + // (7,29): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly 'A, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // Console.WriteLine(b.F); + Diagnostic(ErrorCode.ERR_NoTypeDef, "F").WithArguments("A", "A, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 29)); + } + + [Fact] + public void MemberRefMetadataDecoder_FindFieldBySignature() + { + var sourceA = +@".class public sealed R extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .field private !0 F1 + .field public !0& F1 + .field private !0& modopt(int32) F2 + .field public !0& modopt(object) F2 + .field private int32& F3 + .field public int8& F3 +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class B +{ + static object F1() => new R().F1; + static object F2() => new R().F2; + static int F3() => new R().F3; }"; var verifier = CompileAndVerify(sourceB, new[] { refA }, verify: Verification.Skipped); // MemberRefMetadataDecoder.FindFieldBySignature() is used to find fields when realIL: true. - verifier.VerifyIL("Program.Main", realIL: true, expectedIL: + verifier.VerifyIL("B.F1", realIL: true, expectedIL: @"{ // Code size 16 (0x10) .maxstack 1 - .locals init (R V_0) + .locals init (R V_0) IL_0000: ldloca.s V_0 - IL_0002: initobj ""R"" + IL_0002: initobj ""R"" IL_0008: ldloc.0 - IL_0009: ldfld ""ref int R.F"" - IL_000e: ldind.i4 + IL_0009: ldfld ""ref object R.F1"" + IL_000e: ldind.ref IL_000f: ret }"); + var refB = verifier.Compilation.EmitToImageReference(); + + var comp = CreateCompilation("", references: new[] { refA, refB }); + comp.VerifyEmitDiagnostics(); + // Call MemberRefMetadataDecoder.FindFieldBySignature() indirectly from MetadataDecoder.GetSymbolForILToken(). - var module = (PEModuleSymbol)((CSharpCompilation)verifier.Compilation).GetReferencedAssemblySymbol(refA).Modules[0]; + var module = (PEModuleSymbol)comp.GetReferencedAssemblySymbol(refB).Modules[0]; + var decoder = new MetadataDecoder(module); var reader = module.Module.MetadataReader; - var fields = reader.FieldDefinitions. - Where(handle => reader.GetString(reader.GetFieldDefinition(handle).Name) == "F"). - Select(handle => decoder.GetSymbolForILToken(handle)).ToArray(); - AssertEx.Equal(new[] { "ref System.Int32 R.F", "ref modopt(System.Int32) T R.F", "ref modopt(System.Object) T R.F" }, fields.ToTestDisplayStrings()); + var fieldReferences = reader.MemberReferences. + Where(handle => reader.GetString(reader.GetMemberReference(handle).Name) is "F1" or "F2" or "F3"). + Select(handle => decoder.GetSymbolForILToken(handle)). + ToArray(); + + var containingType = fieldReferences[0].ContainingType; + var fieldMembers = containingType.GetMembers().WhereAsArray(m => m.Kind == SymbolKind.Field); + var expectedMembers = new[] + { + "System.Object R.F1", + "ref System.Object R.F1", + "ref modopt(System.Int32) System.Object R.F2", + "ref modopt(System.Object) System.Object R.F2", + "ref System.Int32 R.F3", + "ref System.SByte R.F3" + }; + AssertEx.Equal(expectedMembers, fieldMembers.ToTestDisplayStrings()); + + var expectedReferences = new[] + { + "ref System.Object R.F1", + "ref modopt(System.Object) System.Object R.F2", + "ref System.SByte R.F3" + }; + AssertEx.Equal(expectedReferences, fieldReferences.ToTestDisplayStrings()); } /// diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs index f8a4fc8feda4c..9e5a54a926172 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs @@ -166,7 +166,7 @@ public void FieldDeclaration_04(LanguageVersion languageVersion) [InlineData(LanguageVersionFacts.CSharpNext)] public void Fixed_01(LanguageVersion languageVersion) { - string source = "struct S { fixed ref int F[1]; }"; + string source = "struct S { fixed ref int F1[1]; fixed ref readonly int F2[2]; }"; UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion)); N(SyntaxKind.StructDeclaration); @@ -189,7 +189,7 @@ public void Fixed_01(LanguageVersion languageVersion) } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.IdentifierToken, "F1"); N(SyntaxKind.BracketedArgumentList); { N(SyntaxKind.OpenBracketToken); @@ -206,6 +206,39 @@ public void Fixed_01(LanguageVersion languageVersion) } N(SyntaxKind.SemicolonToken); } + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F2"); + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } N(SyntaxKind.CloseBraceToken); } EOF(); @@ -216,11 +249,14 @@ public void Fixed_01(LanguageVersion languageVersion) [InlineData(LanguageVersionFacts.CSharpNext)] public void Fixed_02(LanguageVersion languageVersion) { - string source = "struct S { ref fixed int F[1]; }"; + string source = "struct S { ref fixed int F1[1]; ref readonly fixed int F2[2]; }"; UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion), - // (1,16): error CS1031: Type expected - // struct S { ref fixed int F[1]; } - Diagnostic(ErrorCode.ERR_TypeExpected, "fixed").WithLocation(1, 16)); + // (1,17): error CS1031: Type expected + // struct S { ref fixed int F1[1]; ref readonly fixed int F2[2]; } + Diagnostic(ErrorCode.ERR_TypeExpected, "fixed").WithLocation(1, 17), + // (1,47): error CS1031: Type expected + // struct S { ref fixed int F1[1]; ref readonly fixed int F2[2]; } + Diagnostic(ErrorCode.ERR_TypeExpected, "fixed").WithLocation(1, 47)); N(SyntaxKind.StructDeclaration); { @@ -249,7 +285,7 @@ public void Fixed_02(LanguageVersion languageVersion) } N(SyntaxKind.VariableDeclarator); { - N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.IdentifierToken, "F1"); N(SyntaxKind.BracketedArgumentList); { N(SyntaxKind.OpenBracketToken); @@ -266,6 +302,46 @@ public void Fixed_02(LanguageVersion languageVersion) } N(SyntaxKind.SemicolonToken); } + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F2"); + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } N(SyntaxKind.CloseBraceToken); } EOF(); diff --git a/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb b/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb index 713040b947b6d..f47b30028edd5 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb @@ -337,11 +337,6 @@ Friend Module Extensions Return DirectCast(this, IMethodSymbol).ReturnsVoid End Function - - Friend Function RefKind(this As ParameterSymbol) As RefKind - Return DirectCast(this, IParameterSymbol).RefKind - End Function - Friend Function ReduceExtensionMethod(this As MethodSymbol, instanceType As TypeSymbol) As MethodSymbol Return this.ReduceExtensionMethod(instanceType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb index 4e0a0366c03f4..c3851cf52f8e7 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb @@ -129,6 +129,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Private Shared Function FindFieldBySignature(targetTypeSymbol As TypeSymbol, targetMemberName As String, customModifiers As ImmutableArray(Of ModifierInfo(Of TypeSymbol)), type As TypeSymbol) As FieldSymbol For Each member In targetTypeSymbol.GetMembers(targetMemberName) Dim field = TryCast(member, FieldSymbol) + ' PROTOTYPE: Need to record RefKind and RefCustomModifiers on PEFieldSymbol + ' to differentiate fields that differ by RefKind or RefCustomModifiers here. + ' See RefFieldTests.MemberRefMetadataDecoder_FindFieldBySignature(). If field IsNot Nothing AndAlso TypeSymbol.Equals(field.Type, type, TypeCompareKind.AllIgnoreOptionsForVB) AndAlso CustomModifiersMatch(field.CustomModifiers, customModifiers) Then diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb index 3a2d720e9a4ef..e2ed4097404b4 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb @@ -2,19 +2,26 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE +Imports Roslyn.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Class RefFieldTests Inherits BasicTestBase + ''' + ''' Ref field in ref struct. + ''' - Public Sub RefField() + Public Sub RefField_01() Dim sourceA = "public ref struct S { - private ref T F; + public ref T F; public S(ref T t) { F = ref t; } }" Dim compA = CreateCSharpCompilation(GetUniqueName(), sourceA, parseOptions:=New CSharp.CSharpParseOptions(languageVersion:=CSharp.LanguageVersion.Preview)) @@ -22,11 +29,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Dim refA = compA.EmitToImageReference() Dim sourceB = -"Class Program - Public Sub Main() +"Imports System +Module Program + Sub Main() Dim s = New S(Of Integer)() + Console.WriteLine(s.F) End Sub -End Class" +End Module" Dim compB = CreateCompilation(sourceB, references:={refA}) compB.AssertTheseDiagnostics( @@ -34,6 +43,115 @@ BC30668: 'S(Of Integer)' is obsolete: 'Types with embedded references are not su Dim s = New S(Of Integer)() ~~~~~~~~~~~~~ ) + + ' PROTOTYPE: RefKind should be RefKind.Ref, or a use-site diagnostic should be generated, or both. + Dim field = compB.GetMember(Of FieldSymbol)("S.F") + VerifyFieldSymbol(field, "S(Of T).F As T", RefKind.None, {}) + Assert.Null(field.GetUseSiteErrorInfo()) + End Sub + + ''' + ''' Ref field in class. + ''' + + Public Sub RefField_02() + Dim sourceA = +".class public A +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .field public !0& modopt(object) modopt(int8) F +}" + Dim refA = CompileIL(sourceA) + + Dim sourceB = +"Imports System +Module Program + Sub Main() + Dim a = New A(Of Integer)() + Console.WriteLine(a.F) + End Sub +End Module" + + Dim comp = CreateCompilation(sourceB, references:={refA}) + ' PROTOTYPE: A use-site diagnostic should be generated. + comp.AssertTheseDiagnostics() + + ' PROTOTYPE: RefKind should be RefKind.Ref and RefCustomModifiers should contain two items, + ' or a use-site diagnostic should be generated, or both. + Dim field = comp.GetMember(Of FieldSymbol)("A.F") + VerifyFieldSymbol(field, "A(Of T).F As T", RefKind.None, {}) + Assert.Null(field.GetUseSiteErrorInfo()) + End Sub + + Private Shared Sub VerifyFieldSymbol(field As FieldSymbol, expectedDisplayString As String, expectedRefKind As RefKind, expectedRefCustomModifiers As String()) + Assert.Equal(expectedRefKind, field.RefKind) + Assert.Equal(expectedRefCustomModifiers, field.RefCustomModifiers.SelectAsArray(Function(m) m.Modifier.ToTestDisplayString())) + Assert.Equal(expectedDisplayString, field.ToTestDisplayString()) + End Sub + + + Public Sub MemberRefMetadataDecoder_FindFieldBySignature() + Dim sourceA = +".class public sealed R extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00) + .field private !0 F1 + .field public !0& F1 + .field private !0& modopt(int32) F2 + .field public !0& modopt(object) F2 + .field private int32& F3 + .field public int8& F3 +}" + Dim refA = CompileIL(sourceA) + + Dim sourceB = +"class B +{ + static object F1() => new R().F1; + static object F2() => new R().F2; + static int F3() => new R().F3; +}" + Dim compB = CreateCSharpCompilation(GetUniqueName(), sourceB, parseOptions:=New CSharp.CSharpParseOptions(languageVersion:=CSharp.LanguageVersion.Preview), referencedAssemblies:={MscorlibRef, refA}) + compB.VerifyDiagnostics() + Dim refB = compB.EmitToImageReference() + + Dim comp = CreateCompilation("", references:={refA, refB}) + comp.AssertNoDiagnostics() + + ' Call MemberRefMetadataDecoder.FindFieldBySignature() indirectly from MetadataDecoder.GetSymbolForILToken(). + Dim [module] = DirectCast(comp.GetReferencedAssemblySymbol(refB).Modules(0), PEModuleSymbol) + Dim decoder = New MetadataDecoder([module]) + Dim reader = [module].Module.MetadataReader + Dim fieldReferences = reader.MemberReferences. + Where(Function(handle) + Dim name = reader.GetString(reader.GetMemberReference(handle).Name) + Return name = "F1" OrElse name = "F2" OrElse name = "F3" + End Function). + Select(Function(handle) decoder.GetSymbolForILToken(handle)). + ToArray() + + Dim containingType = fieldReferences(0).ContainingType + Dim fieldMembers = containingType.GetMembers().WhereAsArray(Function(m) m.Kind = SymbolKind.Field) + Dim expectedMembers = + { + "R(Of System.Object).F1 As System.Object", + "R(Of System.Object).F1 As System.Object", + "R(Of System.Object).F2 As System.Object", + "R(Of System.Object).F2 As System.Object", + "R(Of System.Object).F3 As System.Int32", + "R(Of System.Object).F3 As System.SByte" + } + AssertEx.Equal(expectedMembers, fieldMembers.Select(Function(f) f.ToTestDisplayString())) + + ' PROTOTYPE: Not differentiating fields that differ by RefKind or RefCustomModifiers. + ' See MemberRefMetadataDecoder.FindFieldBySignature(). + Dim expectedReferences = + { + "R(Of System.Object).F1 As System.Object", + "R(Of System.Object).F2 As System.Object", + "R(Of System.Object).F3 As System.SByte" + } + AssertEx.Equal(expectedReferences, fieldReferences.Select(Function(f) f.ToTestDisplayString())) End Sub End Class From e4527ab648c00ae5e31e86542adb65ce143246eb Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 28 Apr 2022 23:05:24 -0700 Subject: [PATCH 28/29] Fix build --- src/Compilers/Test/Utilities/VisualBasic/Extensions.vb | 5 +++++ .../VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb b/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb index f47b30028edd5..713040b947b6d 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb @@ -337,6 +337,11 @@ Friend Module Extensions Return DirectCast(this, IMethodSymbol).ReturnsVoid End Function + + Friend Function RefKind(this As ParameterSymbol) As RefKind + Return DirectCast(this, IParameterSymbol).RefKind + End Function + Friend Function ReduceExtensionMethod(this As MethodSymbol, instanceType As TypeSymbol) As MethodSymbol Return this.ReduceExtensionMethod(instanceType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb index e2ed4097404b4..a3ec47e364646 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb @@ -46,7 +46,7 @@ BC30668: 'S(Of Integer)' is obsolete: 'Types with embedded references are not su ' PROTOTYPE: RefKind should be RefKind.Ref, or a use-site diagnostic should be generated, or both. Dim field = compB.GetMember(Of FieldSymbol)("S.F") - VerifyFieldSymbol(field, "S(Of T).F As T", RefKind.None, {}) + VerifyFieldSymbol(field, "S(Of T).F As T", Microsoft.CodeAnalysis.RefKind.None, {}) Assert.Null(field.GetUseSiteErrorInfo()) End Sub @@ -79,7 +79,7 @@ End Module" ' PROTOTYPE: RefKind should be RefKind.Ref and RefCustomModifiers should contain two items, ' or a use-site diagnostic should be generated, or both. Dim field = comp.GetMember(Of FieldSymbol)("A.F") - VerifyFieldSymbol(field, "A(Of T).F As T", RefKind.None, {}) + VerifyFieldSymbol(field, "A(Of T).F As T", Microsoft.CodeAnalysis.RefKind.None, {}) Assert.Null(field.GetUseSiteErrorInfo()) End Sub From e3fda91defc5840abcc14d756e37e69ffed26767 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Fri, 29 Apr 2022 08:58:43 -0700 Subject: [PATCH 29/29] Add comment --- src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs index 19e2501aba1ef..af1b9a8db8a21 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs @@ -541,9 +541,11 @@ private LocalDefinition EmitInstanceFieldAddress(BoundFieldAccess fieldAccess, A { var field = fieldAccess.FieldSymbol; - //NOTE: we are not propagating AddressKind.Constrained here. - // the reason is that while Constrained permits calls, it does not permit - // taking field addresses, so we have to turn Constrained into writeable. + // NOTE: We are not propagating AddressKind.Constrained here. + // The reason is that while Constrained permits calls, it does not permit + // taking field addresses, so we have to turn Constrained into writeable. + // For ref fields, we only require a readonly address for the receiver + // since we are loading the field value. var tempOpt = EmitReceiverRef( fieldAccess.ReceiverOpt, field.RefKind == RefKind.None ? @@ -553,7 +555,7 @@ private LocalDefinition EmitInstanceFieldAddress(BoundFieldAccess fieldAccess, A _builder.EmitOpCode(field.RefKind == RefKind.None ? ILOpCode.Ldflda : ILOpCode.Ldfld); EmitSymbolToken(field, fieldAccess.Syntax); - // when loading an address of a fixed field, we actually + // When loading an address of a fixed field, we actually // want to load the address of its "FixedElementField" instead. // Both the buffer backing struct and its only field should be at the same location, // so we could in theory just use address of the struct, but in some contexts that causes