diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index b61abacd57cd1..0cbff6bdd5413 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, @@ -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,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 (fieldSymbol.IsReadOnly) + if (fieldSymbol.RefKind == RefKind.None ? RequiresAssignableVariable(valueKind) : RequiresRefAssignableVariable(valueKind)) { var canModifyReadonly = false; @@ -828,6 +828,22 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, return false; } } + } + + if (RequiresAssignableVariable(valueKind)) + { + switch (fieldSymbol.RefKind) + { + case RefKind.None: + break; + case RefKind.Ref: + return true; + case RefKind.RefReadOnly: + ReportReadOnlyError(fieldSymbol, node, valueKind, checkingReceiver, diagnostics); + return false; + default: + throw ExceptionUtilities.UnexpectedValue(fieldSymbol.RefKind); + } if (fieldSymbol.IsFixedSizeBuffer) { @@ -838,8 +854,20 @@ private bool CheckFieldValueKind(SyntaxNode node, BoundFieldAccess fieldAccess, if (RequiresRefAssignableVariable(valueKind)) { - Error(diagnostics, ErrorCode.ERR_RefLocalOrParamExpected, node); - return false; + Debug.Assert(!fieldIsStatic); + Debug.Assert(valueKind == BindValueKind.RefAssignable); + + switch (fieldSymbol.RefKind) + { + case RefKind.None: + Error(diagnostics, ErrorCode.ERR_RefLocalOrParamExpected, node); + return false; + case RefKind.Ref: + case RefKind.RefReadOnly: + return CheckIsValidReceiverForVariable(node, fieldAccess.ReceiverOpt, BindValueKind.Assignable, diagnostics); + default: + throw ExceptionUtilities.UnexpectedValue(fieldSymbol.RefKind); + } } // r/w fields that are static or belong to reference types are writeable and returnable @@ -1890,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)); + 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 @@ -3816,6 +3844,11 @@ private static bool HasHome( return false; } + if (field.RefKind is RefKind.Ref or RefKind.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/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 8e8047e68d00b..1e95b86190f29 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7102,6 +7102,16 @@ protected BoundExpression BindFieldAccess( CheckReceiverAndRuntimeSupportForSymbolAccess(node, receiver, fieldSymbol, diagnostics); } + // 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 checking compilation to + // avoid cycles for source symbols. + if ((object)Compilation.SourceModule != fieldSymbol.OriginalDefinition.ContainingModule && + fieldSymbol.RefKind != RefKind.None) + { + 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/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 9169e5de50d71..1770b8df3dcef 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}'. @@ -6645,6 +6645,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..af1b9a8db8a21 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) { @@ -541,15 +541,21 @@ 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. - var tempOpt = EmitReceiverRef(fieldAccess.ReceiverOpt, addressKind == AddressKind.Constrained ? AddressKind.Writeable : addressKind); - - _builder.EmitOpCode(ILOpCode.Ldflda); + // 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 ? + (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); - // 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 diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index f684af2db356c..47d1879ae1561 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 @@ -1167,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; } @@ -2089,7 +2104,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 @@ -2294,7 +2309,6 @@ private static bool TargetIsNotOnHeap(BoundExpression left) return false; } - private bool EmitAssignmentPreamble(BoundAssignmentOperator assignmentOperator) { var assignmentTarget = assignmentOperator.Left; @@ -2309,7 +2323,12 @@ 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"); @@ -2586,7 +2605,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 +2813,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 +2822,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 +2845,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 2154a48c38d79..3e045b6ee2739 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 72df455b901df..1ef9c30b0879d 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 78051f34da7ac..893440f9fe935 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -254,6 +254,7 @@ internal enum MessageID IDS_FeatureUnsignedRightShift = MessageBase + 12823, IDS_FeatureExtendedNameofScope = MessageBase + 12824, IDS_FeatureRelaxedShiftOperator = MessageBase + 12825, + IDS_FeatureRefFields = MessageBase + 12826, } // Message IDs may refer to strings that need to be localized. @@ -377,6 +378,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureUnsignedRightShift: // semantic check for declarations and consumption, parsing check for doc comments case MessageID.IDS_FeatureExtendedNameofScope: // semantic check case MessageID.IDS_FeatureRelaxedShiftOperator: // semantic check + case MessageID.IDS_FeatureRefFields: // semantic check return LanguageVersion.Preview; // C# 10.0 features. 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/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/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/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 7504624b559aa..e2267d6281127 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 d3939bc460f9a..c2a8689da5027 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..4970ae56b4e6d 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; } /// @@ -344,7 +348,9 @@ internal bool CalculateUseSiteDiagnostic(ref UseSiteInfo result) Debug.Assert(IsDefinition); // Check type, custom modifiers - if (DeriveUseSiteInfoFromType(ref result, this.TypeWithAnnotations, AllowedRequiredModifierType.System_Runtime_CompilerServices_Volatile)) + // PROTOTYPE: Disallow volatile for ref fields. + 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/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs index 027115bf4bc27..0c877aa3df65c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs @@ -138,9 +138,8 @@ 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); + return FindFieldBySignature(_containingType, memberName, fieldInfo); default: // error: unexpected calling convention @@ -153,16 +152,19 @@ 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. 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/PEFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs index 6895447a75757..702c62b020bb9 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,9 @@ 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); + 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,8 +298,13 @@ 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)); + // PROTOTYPE: `fixed ref` field use should be disallowed. TypeSymbol fixedElementType; int fixedSize; if (customModifiersArray.IsEmpty && IsFixedBuffer(out fixedSize, out fixedElementType)) @@ -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..cfd472f4618d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -1151,10 +1151,12 @@ 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); + TypeSymbol type = fieldInfo.Type; - if (type.SpecialType.IsValidEnumUnderlyingType() && !customModifiers.AnyRequired()) + if (type.SpecialType.IsValidEnumUnderlyingType() && + !fieldInfo.IsByRef && + !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 cdb351051f5f0..fc8c53925b77a 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/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/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 44a1763685642..90a9d62102f4b 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 => _underlyingField.RefKind; + + public sealed override ImmutableArray RefCustomModifiers => _underlyingField.RefCustomModifiers; + 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 e21870d20b2ec..d0aa41446808c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1178,8 +1178,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. @@ -1547,6 +1547,11 @@ struktury záznamů 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 94cf477e605ce..73483acaa04f9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1178,8 +1178,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. @@ -1547,6 +1547,11 @@ Datensatzstrukturen 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index c4a80112f4b64..408fd43dbdb57 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1178,8 +1178,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. @@ -1547,6 +1547,11 @@ registros 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index c23e4aeabcbfc..8768988fa3fd7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1178,8 +1178,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. @@ -1547,6 +1547,11 @@ structs d’enregistrement 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 78a11cdff784a..45ef07cde708e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1178,8 +1178,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. @@ -1547,6 +1547,11 @@ struct di record 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 4bf3da72bd4f6..ec3abf5962632 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1178,8 +1178,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 ローカルまたはパラメーターにする必要があります。 @@ -1547,6 +1547,11 @@ レコード構造体 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 36425a035ceed..7a2442ca4c69c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1178,8 +1178,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. + 참조 할당의 왼쪽은 참조 로컬 또는 매개 변수여야 합니다. @@ -1547,6 +1547,11 @@ 레코드 구조체 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 77a1fcee6d8be..250a8af65a674 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1178,8 +1178,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. @@ -1547,6 +1547,11 @@ struktury rekordów 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index fe02142b5a6be..55d4741ecd9ca 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1178,8 +1178,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. @@ -1547,6 +1547,11 @@ registrar structs 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 0da4fd2dd0927..715340bf707f3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1178,8 +1178,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. + Левая часть выражения назначения ссылки должна быть локальной ссылкой или параметром. @@ -1547,6 +1547,11 @@ структуры записей 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index d10d7ae383d3b..665ad9c92c5c0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1178,8 +1178,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. @@ -1547,6 +1547,11 @@ kayıt yapıları 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 7230f1582375c..f5bed52dee29e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1178,8 +1178,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 本地函数或参数。 @@ -1547,6 +1547,11 @@ 记录结构 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 09d43796f52dc..fb68111cd080f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1178,8 +1178,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. + 參考指派的左側必須為參考本機或參數。 @@ -1547,6 +1547,11 @@ 記錄結構 'record structs' is not localizable. + + ref fields + ref fields + + relaxed shift operator relaxed shift operator 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 new file mode 100644 index 0000000000000..f36e8ba43533f --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -0,0 +1,4130 @@ +// 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 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; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class RefFieldTests : CSharpTestBase + { + private static string IncludeExpectedOutput(string expectedOutput) + { +#if NET7_0_OR_GREATER + return expectedOutput; +#else + return null; +#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); + } +}"; + + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); + 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)); + + 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(); + + 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] + [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)); + + VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); + + comp = CreateCompilation(sourceA); + comp.VerifyEmitDiagnostics(); + var refA = AsReference(comp, useCompilationReference); + VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); + + 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); + } +}"; + + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); + 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)); + + 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 +2 +3 +3 +")); + comp = (CSharpCompilation)verifier.Compilation; + VerifyFieldSymbol(comp.GetMember("S.F"), "ref T S.F", RefKind.Ref, new string[0]); + } + + [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)); + + 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); + VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); + + 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); + } +}"; + + comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular10); + 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)); + + 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 +2 +3 +3 +")); + comp = (CSharpCompilation)verifier.Compilation; + VerifyFieldSymbol(comp.GetMember("S.F"), "ref readonly T S.F", RefKind.RefReadOnly, new string[0]); + } + + [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"); + VerifyFieldSymbol(field, "ref modopt(System.SByte) modopt(System.Object) System.Int32 A.F", RefKind.Ref, new[] { "System.SByte", "System.Object" }); + } + + [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"); + // Currently, source symbols cannot declare RefCustomModifiers. If that + // changes, update this test to verify retargeting of RefCutomModifiers. + 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())); + } + + [Fact] + public void FixedField_01() + { + var source = +@"unsafe ref struct S +{ + 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); + 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(); + } + + /// + /// Unexpected modreq(). + /// + [Fact] + public void RefCustomModifiers_UseSiteDiagnostic_02() + { + 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(int32) 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)); + } + + /// + /// 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("B.F1", 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 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)comp.GetReferencedAssemblySymbol(refB).Modules[0]; + + var decoder = new MetadataDecoder(module); + var reader = module.Module.MetadataReader; + 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()); + } + + /// + /// Determination of enum underlying type should ignore ref fields + /// and fields with required custom modifiers. + /// + [Fact] + public void EnumUnderlyingType() + { + var sourceA = +@".class public sealed E extends [mscorlib]System.Enum +{ + .field public int64 modreq(object) value1 + .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); + + 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); + Assert.Equal(expectedRefCustomModifiers, field.RefCustomModifiers.SelectAsArray(m => m.Modifier.ToTestDisplayString())); + Assert.Equal(expectedDisplayString, 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 F(ref T t) + { + 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 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 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 S4().F = ref t; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new S4().F").WithLocation(30, 9)); + } + + [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(ref T t) + { + F = t; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void AssignValueTo_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 = tValue; + F = tRef; + F = tOut; + F = tIn; + } + object P + { + init + { + F = GetValue(); + F = GetRef(); + F = GetRefReadonly(); + } + } + 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(); + } + + [Fact] + public void AssignValueTo_InstanceMethod_RefReadonlyField() + { + 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 = 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), + // (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 AssignValueTo_InstanceMethod_ReadonlyRefField() + { + 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 = tValue; + F = tRef; + F = tOut; + F = tIn; + } + object P + { + init + { + F = GetValue(); + F = GetRef(); + F = GetRefReadonly(); + } + } + 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(); + } + + [Fact] + public void AssignValueTo_InstanceMethod_ReadonlyRefReadonlyField() + { + var source = +@"ref struct S +{ + public readonly ref readonly T F; + public S(T tValue, ref T tRef, out T tOut, in T tIn) + { + tOut = default; + 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), + // (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( + // (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 AssignRefTo_InstanceMethod_RefReadonlyField() + { + 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; + } + object P + { + init + { + F = ref GetRef(); + F = ref GetRefReadonly(); + } + } + 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 AssignRefTo_InstanceMethod_ReadonlyRefField() + { + 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; // 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 }); + // 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 + 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 AssignRefTo_InstanceMethod_ReadonlyRefReadonlyField() + { + var source = +@"ref struct S +{ + public readonly 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; + } + object P + { + init + { + F = ref GetRef(); + F = ref GetRefReadonly(); + } + } + 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 = +@"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 = 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, 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 verifier = CompileAndVerify(source, verify: Verification.Skipped, 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] + 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 = +@"using System; + +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, 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 verifier = CompileAndVerify(source, verify: Verification.Skipped, 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] + 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; } // 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( + // (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 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_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; } // 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; } // 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; } // 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, verify: Verification.Skipped, 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] + 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; } // 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( + // (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 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_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; } // 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; } // 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; } // 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, verify: Verification.Skipped, 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] + 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 AssignOutParameterFrom_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 AssignRefLocalFrom_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( + // (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 AssignRefReadonlyLocalFrom_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] + 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 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; + 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 F3(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(9, 39), + // (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)); + } + + [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 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; + 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 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 F7(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 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; + 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 F3(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(9, 39), + // (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)); + } + + [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 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; + 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 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 F7(S s) => ref s.F; + Diagnostic(ErrorCode.ERR_RefReturnParameter2, "s").WithArguments("s").WithLocation(13, 48)); + } + + [Fact] + public void RefParameter_InstanceMethod_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); + } + 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_InstanceMethod_RefReadonly() + { + var source = +@"ref struct S +{ + public ref readonly T F; + public S(ref T t) + { + F = ref t; + M1(F); + M2(ref F); // 1 + M3(out F); // 2 + M4(F); + M4(in F); + } + object P + { + init + { + M1(F); + M2(ref F); // 3 + M3(out F); // 4 + M4(F); + M4(in F); + } + } + void M() + { + M1(F); + M2(ref F); // 5 + M3(out F); // 6 + M4(F); + M4(in 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); // 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); // 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); // 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); // 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); // 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); // 6 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16)); + } + + [Fact] + public void RefParameter_InstanceMethod_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); + } + 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_InstanceMethod_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); // 1 + M3(out F); // 2 + M4(F); + M4(in F); + } + object P + { + init + { + M1(F); + M2(ref F); // 3 + M3(out F); // 4 + M4(F); + M4(in F); + } + } + void M() + { + M1(F); + M2(ref F); // 5 + M3(out F); // 6 + M4(F); + M4(in 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); // 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); // 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); // 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); // 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); // 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); // 6 + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "F").WithArguments("field", "S.F").WithLocation(28, 16)); + } + + [Fact] + public void RefParameter_Ref() + { + var source = +@"ref struct S +{ + public ref 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); } + 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( + // (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 RefParameter_ReadonlyRef() + { + var source = +@"ref struct S +{ + public readonly ref 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); } + 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( + // (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 RefParameter_ReadonlyRefReadonly_PEVerifyCompat() + { + var source = +@"#pragma warning disable 649 +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(); + } + + [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 r2In) + { + return r2In.R1.F; + } +}"; + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput( +@"42 +42 +42 +")); + verifier.VerifyIL("Program.Read", +@"{ + // Code size 18 (0x12) + .maxstack 1 + 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", +@"{ + // 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() + { + var source = +@"ref struct S +{ + public ref T F; + private int _other; + public S(int other) : this() { _other = other; } +} +class Program +{ + static T ReadWrite1(ref T t) + { + return new S().F = ref t; + } + static T ReadWrite2(ref T t) + { + return new S(1).F = ref t; + } +}"; + 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] + 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; + ReadAndDiscard1(ref i); + ReadAndDiscardNoArg(); + ReadAndDiscard2(new S(ref i)); + } + static void ReadAndDiscard1(ref T t) + { + _ = new S(ref t).F; + } + 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.ReadAndDiscard1", +@"{ + // Code size 8 (0x8) + .maxstack 1 + IL_0000: ldarg.0 + 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 +}"); + verifier.VerifyIL("Program.ReadAndDiscard2", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: 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; + 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( + // (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(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] + 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; + 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")); + 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] + 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; + 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 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] + 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; + i = RefReadonlyReturn(out s, ref i); + Console.WriteLine(i); + } + 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")); + var expectedIL = +@"{ + // 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 +}"; + 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() + { + 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 +33 +33 +")); + 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 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); + 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; + } +}"; + 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] + 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 = ref t; + Q = ref 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); + 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). + 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), + // (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), + // (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), + // (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 variable. + // s.Q = ref x; + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.Q").WithLocation(27, 9)); + } + + [Fact] + public void RefAccessor_Value() + { + 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); + 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() + { + 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 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; + Diagnostic(ErrorCode.ERR_RefReturnStructThis, "t").WithArguments("this").WithLocation(5, 31)); + } + } +} 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)); 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..9e5a54a926172 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs @@ -0,0 +1,461 @@ +// 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(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void Fixed_01(LanguageVersion languageVersion) + { + string source = "struct S { fixed ref int F1[1]; fixed ref readonly int F2[2]; }"; + 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, "F1"); + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + 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(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersionFacts.CSharpNext)] + public void Fixed_02(LanguageVersion languageVersion) + { + string source = "struct S { ref fixed int F1[1]; ref readonly fixed int F2[2]; }"; + UsingDeclaration(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (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); + { + 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, "F1"); + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + 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(); + } + + [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)] + [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..c5cfeeece1247 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), @@ -425,9 +419,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 123739a331653..e65375f0206bf 100644 --- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs +++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs @@ -432,6 +432,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/CodeGen/ReferenceDependencyWalker.cs b/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs index 0bf8aa2841a21..1e2a999910fbd 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/Emit/NoPia/CommonEmbeddedField.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedField.cs index e12d08c6ba8f5..0b776e67b1612 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 => UnderlyingField.RefCustomModifiers; + + bool Cci.IFieldReference.IsByReference => UnderlyingField.IsByReference; + 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..d88dc304b4f4f 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 FieldInfo DecodeFieldSignature(FieldDefinitionHandle fieldHandle) { try { @@ -1895,40 +1916,50 @@ internal TypeSymbol DecodeFieldSignature(FieldDefinitionHandle fieldHandle, out if (signatureHeader.Kind != SignatureKind.Field) { - customModifiers = default(ImmutableArray>); - return GetUnsupportedMetadataTypeSymbol(); // unsupported signature content + return new FieldInfo(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content + } + else + { + return DecodeFieldSignature(ref signatureReader); } - - return DecodeFieldSignature(ref signatureReader, out customModifiers); } catch (BadImageFormatException mrEx) { - customModifiers = default(ImmutableArray>); - return GetUnsupportedMetadataTypeSymbol(mrEx); + return new FieldInfo(GetUnsupportedMetadataTypeSymbol(mrEx)); } } // MetaImport::DecodeFieldSignature - protected TypeSymbol DecodeFieldSignature(ref BlobReader signatureReader, out ImmutableArray> customModifiers) + protected FieldInfo DecodeFieldSignature(ref BlobReader signatureReader) { - customModifiers = 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 _); + return new FieldInfo(isByRef, refCustomModifiers, type, customModifiers); } catch (UnsupportedSignatureContent) { - return GetUnsupportedMetadataTypeSymbol(); // unsupported signature content + return new FieldInfo(GetUnsupportedMetadataTypeSymbol()); // unsupported signature content } catch (BadImageFormatException mrEx) { - return 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 fcb78a61af057..ca10b4a52cf01 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 contains a managed pointer. + /// + bool IsByReference { get; } + /// /// The Field being referred to. /// 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 17defcf93881b..506992927a02f 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -3336,7 +3336,16 @@ private void SerializeParameterInformation(ParameterTypeEncoder encoder, IParame private void SerializeFieldSignature(IFieldReference fieldReference, BlobBuilder builder) { + 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). var typeEncoder = new BlobEncoder(builder).FieldSignature(); + SerializeCustomModifiers(new CustomModifiersEncoder(builder), fieldReference.RefCustomModifiers); + if (fieldReference.IsByReference) + { + typeEncoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference); + } SerializeTypeReference(typeEncoder, fieldReference.GetType(Context)); } 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); diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 42be894990e61..2b2346b71f7f8 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,6 +1,8 @@ *REMOVED*override abstract Microsoft.CodeAnalysis.Diagnostic.Equals(object? obj) -> bool abstract Microsoft.CodeAnalysis.SymbolVisitor.DefaultResult.get -> TResult 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.IImportScope Microsoft.CodeAnalysis.IImportScope.Aliases.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.IImportScope.ExternAliases.get -> System.Collections.Immutable.ImmutableArray 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/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index c76f113040c23..d22002f2ab12d 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) { 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 88924bd0e60d8..5b57761a0dd15 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..c3851cf52f8e7 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.vb @@ -114,9 +114,8 @@ 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) = Me.DecodeFieldSignature(signaturePointer) + Return FindFieldBySignature(_containingType, memberName, fieldInfo.CustomModifiers, fieldInfo.Type) Case Else ' error @@ -130,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/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb index bb09dc34f1427..46fcf249f7f93 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEFieldSymbol.vb @@ -347,12 +347,14 @@ 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) = New MetadataDecoder(moduleSymbol, _containingType).DecodeFieldSignature(_handle) + 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/Semantic/Semantics/RefFieldTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb new file mode 100644 index 0000000000000..a3ec47e364646 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/RefFieldTests.vb @@ -0,0 +1,159 @@ +' 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 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_01() + Dim sourceA = +"public ref struct S +{ + 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)) + compA.VerifyDiagnostics() + Dim refA = compA.EmitToImageReference() + + Dim sourceB = +"Imports System +Module Program + Sub Main() + Dim s = New S(Of Integer)() + Console.WriteLine(s.F) + End Sub +End Module" + + 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)() + ~~~~~~~~~~~~~ +) + + ' 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", Microsoft.CodeAnalysis.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", Microsoft.CodeAnalysis.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 + +End Namespace 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/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 7a502c4e71394..15817c8676b55 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -7071,6 +7071,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 08db582288b0b..e827b7d6adc47 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationFieldSymbol.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationFieldSymbol.cs @@ -82,6 +82,10 @@ public bool IsReadOnly public int FixedSize => 0; + public RefKind RefKind => RefKind.None; + + public ImmutableArray RefCustomModifiers => ImmutableArray.Empty; + public ImmutableArray CustomModifiers { get