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 @@
-
- Levá strana přiřazení odkazu musí být lokální proměnná nebo parametr odkazu.
+
+ 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
+
+ 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 @@
-
- Die linke Seite einer ref-Zuweisung muss ein lokaler Verweis oder ein Parameter sein.
+
+ 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
+
+ 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 @@
-
- La parte izquierda de una asignación de referencias debe ser una referencia local o un parámetro.
+
+ 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
+
+ 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 @@
-
- La partie gauche d'une assignation par référence doit être une variable locale ou un paramètre ref.
+
+ 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
+
+ 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 @@
-
- La parte sinistra di un'assegnazione ref deve essere un parametro o una variabile locale ref.
+
+ 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
+
+ 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 @@
-
- ref 代入の左辺は、ref ローカルまたはパラメーターにする必要があります。
+
+ ref 代入の左辺は、ref ローカルまたはパラメーターにする必要があります。
@@ -1547,6 +1547,11 @@
レコード構造体'record structs' is not localizable.
+
+
+ ref fields
+
+ 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 @@
-
- 참조 할당의 왼쪽은 참조 로컬 또는 매개 변수여야 합니다.
+
+ 참조 할당의 왼쪽은 참조 로컬 또는 매개 변수여야 합니다.
@@ -1547,6 +1547,11 @@
레코드 구조체'record structs' is not localizable.
+
+
+ ref fields
+
+ 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 @@
-
- Lewa strona przypisania odwołania musi być odwołaniem lokalnym lub parametrem.
+
+ 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
+
+ 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 @@
-
- O lado esquerdo da atribuição ref precisa ser um parâmetro ou local ref.
+
+ 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
+
+ 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 @@
-
- Левая часть выражения назначения ссылки должна быть локальной ссылкой или параметром.
+
+ Левая часть выражения назначения ссылки должна быть локальной ссылкой или параметром.
@@ -1547,6 +1547,11 @@
структуры записей'record structs' is not localizable.
+
+
+ ref fields
+
+ 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 @@
-
- ref atamasının sol tarafı, yerel ref veya parametresi olmalıdır.
+
+ 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
+
+ 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 @@
-
- ref 赋值左侧必须为 ref 本地函数或参数。
+
+ ref 赋值左侧必须为 ref 本地函数或参数。
@@ -1547,6 +1547,11 @@
记录结构'record structs' is not localizable.
+
+
+ ref fields
+
+ 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 @@
-
- 參考指派的左側必須為參考本機或參數。
+
+ 參考指派的左側必須為參考本機或參數。
@@ -1547,6 +1547,11 @@
記錄結構'record structs' is not localizable.
+
+
+ ref fields
+
+ 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