diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 0b6699f96cd11..2c5d439ab23ea 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -488,6 +488,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin // dynamic expressions are readwrite, and can even be passed by ref (which is implemented via a temp) case BoundKind.DynamicMemberAccess: case BoundKind.DynamicIndexerAccess: + case BoundKind.DynamicObjectInitializerMember: { if (RequiresRefAssignableVariable(valueKind)) { @@ -3144,7 +3145,11 @@ private uint GetValEscapeOfObjectInitializer(BoundObjectInitializerExpression in if (expression.Kind == BoundKind.AssignmentOperator) { var assignment = (BoundAssignmentOperator)expression; - result = Math.Max(result, GetValEscape(assignment.Right, scopeOfTheContainingExpression)); + var rightValEscape = assignment.IsRef + ? GetRefEscape(assignment.Right, scopeOfTheContainingExpression) + : GetValEscape(assignment.Right, scopeOfTheContainingExpression); + + result = Math.Max(result, rightValEscape); var left = (BoundObjectInitializerMember)assignment.Left; result = Math.Max(result, GetValEscape(left.Arguments, scopeOfTheContainingExpression)); @@ -3773,7 +3778,11 @@ private bool CheckValEscapeOfObjectInitializer(BoundObjectInitializerExpression if (expression.Kind == BoundKind.AssignmentOperator) { var assignment = (BoundAssignmentOperator)expression; - if (!CheckValEscape(expression.Syntax, assignment.Right, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics)) + bool valid = assignment.IsRef + ? CheckRefEscape(expression.Syntax, assignment.Right, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics) + : CheckValEscape(expression.Syntax, assignment.Right, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics); + + if (!valid) { return false; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 98ade059cd5a2..096834571c6e6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -4588,6 +4588,7 @@ private BoundObjectInitializerExpressionBase BindInitializerExpression( private BoundExpression BindInitializerExpressionOrValue( ExpressionSyntax syntax, TypeSymbol type, + BindValueKind rhsValueKind, SyntaxNode typeSyntax, BindingDiagnosticBag diagnostics) { @@ -4599,9 +4600,10 @@ private BoundExpression BindInitializerExpressionOrValue( case SyntaxKind.ObjectInitializerExpression: case SyntaxKind.CollectionInitializerExpression: Debug.Assert(syntax.Parent.Parent.Kind() != SyntaxKind.WithInitializerExpression); + Debug.Assert(rhsValueKind == BindValueKind.RValue); return BindInitializerExpression((InitializerExpressionSyntax)syntax, type, typeSyntax, isForNewInstance: false, diagnostics); default: - return BindValue(syntax, diagnostics, BindValueKind.RValue); + return BindValue(syntax, diagnostics, rhsValueKind); } } @@ -4666,44 +4668,37 @@ private BoundExpression BindInitializerMemberAssignment( { var initializer = (AssignmentExpressionSyntax)memberInitializer; - // Bind member initializer identifier, i.e. left part of assignment - BoundExpression boundLeft = null; - var leftSyntax = initializer.Left; + // We use a location specific binder for binding object initializer field/property access to generate object initializer specific diagnostics: + // 1) CS1914 (ERR_StaticMemberInObjectInitializer) + // 2) CS1917 (ERR_ReadonlyValueTypeInObjectInitializer) + // 3) CS1918 (ERR_ValueTypePropertyInObjectInitializer) + // See comments in BindObjectInitializerExpression for more details. - if (initializerType.IsDynamic() && leftSyntax.Kind() == SyntaxKind.IdentifierName) - { - { - // D = { ..., = , ... }, where D : dynamic - var memberName = ((IdentifierNameSyntax)leftSyntax).Identifier.Text; - boundLeft = new BoundDynamicObjectInitializerMember(leftSyntax, memberName, implicitReceiver.Type, initializerType, hasErrors: false); - } - } - else - { - // We use a location specific binder for binding object initializer field/property access to generate object initializer specific diagnostics: - // 1) CS1914 (ERR_StaticMemberInObjectInitializer) - // 2) CS1917 (ERR_ReadonlyValueTypeInObjectInitializer) - // 3) CS1918 (ERR_ValueTypePropertyInObjectInitializer) - // See comments in BindObjectInitializerExpression for more details. + Debug.Assert(objectInitializerMemberBinder != null); - Debug.Assert(objectInitializerMemberBinder != null); - - boundLeft = objectInitializerMemberBinder.BindObjectInitializerMember(initializer, implicitReceiver, diagnostics); - } + BoundExpression boundLeft = objectInitializerMemberBinder.BindObjectInitializerMember(initializer, implicitReceiver, diagnostics); if (boundLeft != null) { Debug.Assert((object)boundLeft.Type != null); + var rhsExpr = initializer.Right.CheckAndUnwrapRefExpression(diagnostics, out RefKind refKind); + bool isRef = refKind == RefKind.Ref; + var rhsKind = isRef ? GetRequiredRHSValueKindForRefAssignment(boundLeft) : BindValueKind.RValue; + // Bind member initializer value, i.e. right part of assignment BoundExpression boundRight = BindInitializerExpressionOrValue( - syntax: initializer.Right, + syntax: rhsExpr, type: boundLeft.Type, + rhsKind, typeSyntax: boundLeft.Syntax, diagnostics: diagnostics); // Bind member initializer assignment expression - return BindAssignment(initializer, boundLeft, boundRight, isRef: false, diagnostics); + // We don't verify escape safety of initializers against the instance because the initializers + // get factored in when determining the safe-to-escape of the instance (the initializers contribute + // like constructor arguments). + return BindAssignment(initializer, boundLeft, boundRight, isRef, verifyEscapeSafety: false, diagnostics); } } @@ -4722,43 +4717,60 @@ private BoundExpression BindObjectInitializerMember( LookupResultKind resultKind; bool hasErrors; - if (namedAssignment.Left.Kind() == SyntaxKind.IdentifierName) - { - var memberName = (IdentifierNameSyntax)namedAssignment.Left; + var leftSyntax = namedAssignment.Left; + var initializerType = implicitReceiver.Type; + SyntaxKind rhsKind = namedAssignment.Right.Kind(); + bool isRef = rhsKind is SyntaxKind.RefExpression; + bool isRhsNestedInitializer = rhsKind is SyntaxKind.ObjectInitializerExpression or SyntaxKind.CollectionInitializerExpression; + BindValueKind valueKind = isRhsNestedInitializer ? BindValueKind.RValue : (isRef ? BindValueKind.RefAssignable : BindValueKind.Assignable); - // SPEC: Each member initializer must name an accessible field or property of the object being initialized, followed by an equals sign and - // SPEC: an expression or an object initializer or collection initializer. - // SPEC: A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment (7.17.1) to the field or property. + if (leftSyntax.Kind() == SyntaxKind.IdentifierName) + { + var memberName = (IdentifierNameSyntax)leftSyntax; - // SPEC VIOLATION: Native compiler also allows initialization of field-like events in object initializers, so we allow it as well. - - boundMember = BindInstanceMemberAccess( - node: memberName, - right: memberName, - boundLeft: implicitReceiver, - rightName: memberName.Identifier.ValueText, - rightArity: 0, - typeArgumentsSyntax: default(SeparatedSyntaxList), - typeArgumentsWithAnnotations: default(ImmutableArray), - invoked: false, - indexed: false, - diagnostics: diagnostics); + if (initializerType.IsDynamic()) + { + // D = { ..., = , ... }, where D : dynamic + boundMember = new BoundDynamicObjectInitializerMember(leftSyntax, memberName.Identifier.Text, implicitReceiver.Type, initializerType, hasErrors: false); + return CheckValue(boundMember, valueKind, diagnostics); + } + else + { + // SPEC: Each member initializer must name an accessible field or property of the object being initialized, followed by an equals sign and + // SPEC: an expression or an object initializer or collection initializer. + // SPEC: A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment (7.17.1) to the field or property. + + // SPEC VIOLATION: Native compiler also allows initialization of field-like events in object initializers, so we allow it as well. + + boundMember = BindInstanceMemberAccess( + node: memberName, + right: memberName, + boundLeft: implicitReceiver, + rightName: memberName.Identifier.ValueText, + rightArity: 0, + typeArgumentsSyntax: default, + typeArgumentsWithAnnotations: default, + invoked: false, + indexed: false, + diagnostics: diagnostics); - resultKind = boundMember.ResultKind; - hasErrors = boundMember.HasAnyErrors || implicitReceiver.HasAnyErrors; + hasErrors = boundMember.HasAnyErrors || implicitReceiver.HasAnyErrors; - if (boundMember.Kind == BoundKind.PropertyGroup) - { - boundMember = BindIndexedPropertyAccess((BoundPropertyGroup)boundMember, mustHaveAllOptionalParameters: true, diagnostics: diagnostics); - if (boundMember.HasAnyErrors) + if (boundMember.Kind == BoundKind.PropertyGroup) { - hasErrors = true; + boundMember = BindIndexedPropertyAccess((BoundPropertyGroup)boundMember, mustHaveAllOptionalParameters: true, diagnostics: diagnostics); + if (boundMember.HasAnyErrors) + { + hasErrors = true; + } } } + + resultKind = boundMember.ResultKind; } - else if (namedAssignment.Left.Kind() == SyntaxKind.ImplicitElementAccess) + else if (leftSyntax.Kind() == SyntaxKind.ImplicitElementAccess) { - var implicitIndexing = (ImplicitElementAccessSyntax)namedAssignment.Left; + var implicitIndexing = (ImplicitElementAccessSyntax)leftSyntax; boundMember = BindElementAccess(implicitIndexing, implicitReceiver, implicitIndexing.ArgumentList, diagnostics); resultKind = boundMember.ResultKind; @@ -4784,15 +4796,12 @@ private BoundExpression BindObjectInitializerMember( // TODO: If/when we have a way to version warnings, we should add a warning for this. BoundKind boundMemberKind = boundMember.Kind; - SyntaxKind rhsKind = namedAssignment.Right.Kind(); - bool isRhsNestedInitializer = rhsKind == SyntaxKind.ObjectInitializerExpression || rhsKind == SyntaxKind.CollectionInitializerExpression; - BindValueKind valueKind = isRhsNestedInitializer ? BindValueKind.RValue : BindValueKind.Assignable; ImmutableArray arguments = ImmutableArray.Empty; - ImmutableArray argumentNamesOpt = default(ImmutableArray); - ImmutableArray argsToParamsOpt = default(ImmutableArray); - ImmutableArray argumentRefKindsOpt = default(ImmutableArray); - BitVector defaultArguments = default(BitVector); + ImmutableArray argumentNamesOpt = default; + ImmutableArray argsToParamsOpt = default; + ImmutableArray argumentRefKindsOpt = default; + BitVector defaultArguments = default; bool expanded = false; switch (boundMemberKind) @@ -4805,7 +4814,7 @@ private BoundExpression BindObjectInitializerMember( if (!hasErrors) { // TODO: distinct error code for collection initializers? (Dev11 doesn't have one.) - Error(diagnostics, ErrorCode.ERR_ReadonlyValueTypeInObjectInitializer, namedAssignment.Left, fieldSymbol, fieldSymbol.Type); + Error(diagnostics, ErrorCode.ERR_ReadonlyValueTypeInObjectInitializer, leftSyntax, fieldSymbol, fieldSymbol.Type); hasErrors = true; } @@ -4818,14 +4827,14 @@ private BoundExpression BindObjectInitializerMember( break; case BoundKind.PropertyAccess: - hasErrors |= isRhsNestedInitializer && !CheckNestedObjectInitializerPropertySymbol(((BoundPropertyAccess)boundMember).PropertySymbol, namedAssignment.Left, diagnostics, hasErrors, ref resultKind); + hasErrors |= isRhsNestedInitializer && !CheckNestedObjectInitializerPropertySymbol(((BoundPropertyAccess)boundMember).PropertySymbol, leftSyntax, diagnostics, hasErrors, ref resultKind); break; case BoundKind.IndexerAccess: { var indexer = BindIndexerDefaultArguments((BoundIndexerAccess)boundMember, valueKind, diagnostics); boundMember = indexer; - hasErrors |= isRhsNestedInitializer && !CheckNestedObjectInitializerPropertySymbol(indexer.Indexer, namedAssignment.Left, diagnostics, hasErrors, ref resultKind); + hasErrors |= isRhsNestedInitializer && !CheckNestedObjectInitializerPropertySymbol(indexer.Indexer, leftSyntax, diagnostics, hasErrors, ref resultKind); arguments = indexer.Arguments; argumentNamesOpt = indexer.ArgumentNamesOpt; argsToParamsOpt = indexer.ArgsToParamsOpt; @@ -4854,6 +4863,9 @@ private BoundExpression BindObjectInitializerMember( break; } + case BoundKind.DynamicObjectInitializerMember: + break; + case BoundKind.DynamicIndexerAccess: { var indexer = (BoundDynamicIndexerAccess)boundMember; @@ -4866,10 +4878,10 @@ private BoundExpression BindObjectInitializerMember( case BoundKind.ArrayAccess: case BoundKind.PointerElementAccess: - return boundMember; + return CheckValue(boundMember, valueKind, diagnostics); default: - return BadObjectInitializerMemberAccess(boundMember, implicitReceiver, namedAssignment.Left, diagnostics, valueKind, hasErrors); + return BadObjectInitializerMemberAccess(boundMember, implicitReceiver, leftSyntax, diagnostics, valueKind, hasErrors); } if (!hasErrors) @@ -4885,7 +4897,7 @@ private BoundExpression BindObjectInitializerMember( } return new BoundObjectInitializerMember( - namedAssignment.Left, + leftSyntax, boundMember.ExpressionSymbol, arguments, argumentNamesOpt, @@ -5200,7 +5212,7 @@ private BoundExpression BindCollectionInitializerElement( Error(diagnostics, ErrorCode.ERR_InvalidInitializerElementInitializer, elementInitializer); } - var boundElementInitializer = BindInitializerExpressionOrValue(elementInitializer, initializerType, implicitReceiver.Syntax, diagnostics); + var boundElementInitializer = BindInitializerExpressionOrValue(elementInitializer, initializerType, BindValueKind.RValue, implicitReceiver.Syntax, diagnostics); BoundExpression result = BindCollectionInitializerElementAddMethod( elementInitializer, @@ -5422,7 +5434,6 @@ protected BoundExpression BindClassCreationExpression( TypeSymbol initializerTypeOpt = null, bool wasTargetTyped = false) { - BoundExpression result = null; bool hasErrors = type.IsErrorType(); if (type.IsAbstract) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 4e0483caaf255..645d243b43017 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1445,7 +1445,6 @@ private BoundExpression BindAssignment(AssignmentExpressionSyntax node, BindingD } BindValueKind lhsKind; - BindValueKind rhsKind; ExpressionSyntax rhsExpr; bool isRef = false; @@ -1453,32 +1452,18 @@ private BoundExpression BindAssignment(AssignmentExpressionSyntax node, BindingD { isRef = true; lhsKind = BindValueKind.RefAssignable; - rhsKind = BindValueKind.RefersToLocation; rhsExpr = ((RefExpressionSyntax)node.Right).Expression; } else { lhsKind = BindValueKind.Assignable; - rhsKind = BindValueKind.RValue; rhsExpr = node.Right; } var op1 = BindValue(node.Left, diagnostics, lhsKind); ReportSuppressionIfNeeded(op1, diagnostics); - var lhsRefKind = RefKind.None; - // If the LHS is a ref (not ref-readonly), the rhs - // must also be value-assignable - if (lhsKind == BindValueKind.RefAssignable && !op1.HasErrors) - { - // We should now know that op1 is a valid lvalue - lhsRefKind = op1.GetRefKind(); - if (lhsRefKind == RefKind.Ref || lhsRefKind == RefKind.Out) - { - rhsKind |= BindValueKind.Assignable; - } - } - + var rhsKind = isRef ? GetRequiredRHSValueKindForRefAssignment(op1) : BindValueKind.RValue; var op2 = BindValue(rhsExpr, diagnostics, rhsKind); if (op1.Kind == BoundKind.DiscardExpression) @@ -1487,7 +1472,26 @@ private BoundExpression BindAssignment(AssignmentExpressionSyntax node, BindingD op1 = InferTypeForDiscardAssignment((BoundDiscardExpression)op1, op2, diagnostics); } - return BindAssignment(node, op1, op2, isRef, diagnostics); + return BindAssignment(node, op1, op2, isRef, verifyEscapeSafety: true, diagnostics); + } + + private static BindValueKind GetRequiredRHSValueKindForRefAssignment(BoundExpression boundLeft) + { + var rhsKind = BindValueKind.RefersToLocation; + + if (!boundLeft.HasErrors) + { + // We should now know that boundLeft is a valid lvalue + var lhsRefKind = boundLeft.GetRefKind(); + if (lhsRefKind is RefKind.Ref or RefKind.Out) + { + // If the LHS is a ref (not ref-readonly), the RHS + // must also be value-assignable + rhsKind |= BindValueKind.Assignable; + } + } + + return rhsKind; } private BoundExpression InferTypeForDiscardAssignment(BoundDiscardExpression op1, BoundExpression op2, BindingDiagnosticBag diagnostics) @@ -1511,6 +1515,7 @@ private BoundAssignmentOperator BindAssignment( BoundExpression op1, BoundExpression op2, bool isRef, + bool verifyEscapeSafety, BindingDiagnosticBag diagnostics) { Debug.Assert(op1 != null); @@ -1538,21 +1543,24 @@ private BoundAssignmentOperator BindAssignment( op2 = BindToNaturalType(op2, diagnostics); } - if (isRef) + if (verifyEscapeSafety) { - var leftEscape = GetRefEscape(op1, LocalScopeDepth); - var rightEscape = GetRefEscape(op2, LocalScopeDepth); - if (leftEscape < rightEscape) + if (isRef) { - Error(diagnostics, ErrorCode.ERR_RefAssignNarrower, node, op1.ExpressionSymbol.Name, op2.Syntax); - op2 = ToBadExpression(op2); + var leftEscape = GetRefEscape(op1, LocalScopeDepth); + var rightEscape = GetRefEscape(op2, LocalScopeDepth); + if (leftEscape < rightEscape) + { + Error(diagnostics, ErrorCode.ERR_RefAssignNarrower, node, getName(op1), op2.Syntax); + op2 = ToBadExpression(op2); + } } - } - if (op1.Type.IsRefLikeType) - { - var leftEscape = GetValEscape(op1, LocalScopeDepth); - op2 = ValidateEscape(op2, leftEscape, isByRef: false, diagnostics); + if (op1.Type.IsRefLikeType) + { + var leftEscape = GetValEscape(op1, LocalScopeDepth); + op2 = ValidateEscape(op2, leftEscape, isByRef: false, diagnostics); + } } } else @@ -1573,6 +1581,25 @@ private BoundAssignmentOperator BindAssignment( } return new BoundAssignmentOperator(node, op1, op2, isRef, type, hasErrors); + + static object getName(BoundExpression expr) + { + if (expr.ExpressionSymbol is { Name: var name }) + { + return name; + } + if (expr is BoundArrayAccess) + { + return MessageID.IDS_ArrayAccess.Localize(); + } + if (expr is BoundPointerElementAccess) + { + return MessageID.IDS_PointerElementAccess.Localize(); + } + + Debug.Assert(false); + return ""; + } } internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out BoundExpression receiver, out SyntaxNode propertySyntax) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs index e91703598a061..74ed17495cc08 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -35,6 +36,19 @@ public static RefKind GetRefKind(this BoundExpression node) case BoundKind.PropertyAccess: return ((BoundPropertyAccess)node).PropertySymbol.RefKind; + case BoundKind.ObjectInitializerMember: + var member = (BoundObjectInitializerMember)node; + if (member.HasErrors) + return RefKind.None; + + return member.MemberSymbol switch + { + FieldSymbol f => f.RefKind, + PropertySymbol f => f.RefKind, + EventSymbol => RefKind.None, + var s => throw ExceptionUtilities.UnexpectedValue(s?.Kind) + }; + default: return RefKind.None; } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 18c2985c379d8..dd4aefb0a2b62 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7187,4 +7187,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ file types + + array access + + + pointer element access + diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 17f7df349245d..10934d0db6beb 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -257,6 +257,8 @@ internal enum MessageID IDS_FeatureRequiredMembers = MessageBase + 12825, IDS_FeatureRefFields = MessageBase + 12826, IDS_FeatureFileTypes = MessageBase + 12827, + IDS_ArrayAccess = MessageBase + 12828, + IDS_PointerElementAccess = MessageBase + 12829, } // Message IDs may refer to strings that need to be localized. diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index b16fda396c9e7..721bb5f3e601e 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -12753,10 +12753,14 @@ private ExpressionSyntax ParseObjectOrCollectionInitializerMember(ref bool isObj { if (this.IsComplexElementInitializer()) { + // { ... } return this.ParseComplexElementInitializer(); } else if (IsDictionaryInitializer()) { + // [...] = { ... } + // [...] = ref + // [...] = isObjectInitializer = true; var initializer = this.ParseDictionaryInitializer(); initializer = CheckFeatureAvailability(initializer, MessageID.IDS_FeatureDictionaryInitializer); @@ -12764,12 +12768,17 @@ private ExpressionSyntax ParseObjectOrCollectionInitializerMember(ref bool isObj } else if (this.IsNamedAssignment()) { + // Name = { ... } + // Name = ref + // Name = isObjectInitializer = true; return this.ParseObjectInitializerNamedAssignment(); } else { - return this.ParseExpressionCore(); + // + // ref + return this.ParsePossibleRefExpression(); } } @@ -12793,7 +12802,7 @@ private ExpressionSyntax ParseObjectInitializerNamedAssignment() } else { - expression = this.ParseExpressionCore(); + expression = this.ParsePossibleRefExpression(); } return _syntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, identifier, equal, expression); @@ -12805,7 +12814,7 @@ private ExpressionSyntax ParseDictionaryInitializer() var equal = this.EatToken(SyntaxKind.EqualsToken); var expression = this.CurrentToken.Kind == SyntaxKind.OpenBraceToken ? this.ParseObjectOrCollectionInitializer() - : this.ParseExpressionCore(); + : this.ParsePossibleRefExpression(); var elementAccess = _syntaxFactory.ImplicitElementAccess(arguments); return _syntaxFactory.AssignmentExpression( diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 9a06122e5fad1..938e31049dea5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1602,6 +1602,11 @@ Direktiva using se dříve zobrazovala jako globální direktiva using + + array access + array access + + async method builder override přepsání tvůrce asynchronní metody @@ -1767,6 +1772,11 @@ pattern matching ReadOnly/Span<char> on constant string + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Sestavení {0}, které obsahuje typ {1}, se odkazuje na architekturu .NET Framework, což se nepodporuje. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 612388afaf72c..1697de3432f8d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1602,6 +1602,11 @@ Die Verwenden-Anweisung wurde zuvor als „Global verwenden“ angezeigt + + array access + array access + + async method builder override Außerkraftsetzung des asynchronen Methoden-Generators @@ -1767,6 +1772,11 @@ Musterabgleich ReadOnly/Span<char> bei konstanter Zeichenfolge + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Die Assembly "{0}" mit dem Typ "{1}" verweist auf das .NET Framework. Dies wird nicht unterstützt. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 6811c80b0bef5..5778f00a7de72 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1602,6 +1602,11 @@ La directiva using aparecía anteriormente como using global + + array access + array access + + async method builder override invalidación del generador de métodos asincrónicos @@ -1767,6 +1772,11 @@ patrón que coincide con ReadOnly/Span<char> en una cadena constante + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. El ensamblado "{0}" que contiene el tipo "{1}" hace referencia a .NET Framework, lo cual no se admite. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 418facb9918b9..4f0faabd94fac 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1602,6 +1602,11 @@ La directive using est apparue précédemment comme using global + + array access + array access + + async method builder override Remplacement du générateur de méthode asynchrone @@ -1767,6 +1772,11 @@ modèle correspondant à ReadOnly/Span<char> sur une chaîne constante + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. L'assembly '{0}' contenant le type '{1}' référence le .NET Framework, ce qui n'est pas pris en charge. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ba419183967ee..6875dba223f14 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1602,6 +1602,11 @@ La direttiva using è già presente come using globale + + array access + array access + + async method builder override override del generatore di metodi asincroni @@ -1767,6 +1772,11 @@ criterio corrispondente a ReadOnly/Span<char> su stringa costante + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. L'assembly '{0}' che contiene il tipo '{1}' fa riferimento a .NET Framework, che non è supportato. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index c3b893f38f869..4b140b9d62eb0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1602,6 +1602,11 @@ using ディレクティブは、以前に global using として使用されています + + array access + array access + + async method builder override 非同期メソッド ビルダーのオーバーライド @@ -1767,6 +1772,11 @@ 定数文字列の ReadOnly/Span<char> に一致するパターン + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 型 '{1}' を含むアセンブリ '{0}' が .NET Framework を参照しています。これはサポートされていません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index c89dff459f03a..3d333a7816e3e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1602,6 +1602,11 @@ using 지시문은 이전에 전역 using으로 나타났습니다. + + array access + array access + + async method builder override 비동기 메서드 빌더 재정의 @@ -1767,6 +1772,11 @@ 상수 문자열에서 ReadOnly/Span<char>과 일치하는 패턴 + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. '{1}' 형식을 포함하는 '{0}' 어셈블리가 지원되지 않는 .NET Framework를 참조합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index c1c996b81fcd4..07300af03d9ca 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1602,6 +1602,11 @@ Dyrektywa użycia pojawiła się wcześniej jako użycie globalne + + array access + array access + + async method builder override zastąpienie konstruktora metodą asynchroniczną @@ -1767,6 +1772,11 @@ pattern matching ReadOnly/Span<char> on constant string + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Zestaw „{0}” zawierający typ „{1}” odwołuje się do platformy .NET Framework, co nie jest obsługiwane. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 441149d3dc25f..fbe4cbc964cd6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1602,6 +1602,11 @@ A diretiva using apareceu anteriormente como using global + + array access + array access + + async method builder override substituição do construtor de método assíncrono @@ -1767,6 +1772,11 @@ padrões correspondentes a ReadOnly/Span<char> na cadeia de caracteres constante + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. O assembly '{0}' contendo o tipo '{1}' referencia o .NET Framework, mas não há suporte para isso. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 15223ef937245..318531f293d5f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1602,6 +1602,11 @@ Директива using ранее использовалась в качестве глобальной + + array access + array access + + async method builder override переопределение построителя методов async @@ -1767,6 +1772,11 @@ сопоставление шаблонов ReadOnly/Span<char> в строке константы + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Сборка "{0}", содержащая тип "{1}", ссылается на платформу .NET Framework, которая не поддерживается. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 5685ac56d476a..ba836b0208bbd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1602,6 +1602,11 @@ Daha önce genel kullanım olarak görünen kullanım yönergesi + + array access + array access + + async method builder override zaman uyumsuz yöntem oluşturucusunu geçersiz kılma @@ -1767,6 +1772,11 @@ sabit dizede ReadOnly/Span<char> ile eşleşen desen + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. '{1}' türünü içeren '{0}' bütünleştirilmiş kodu, desteklenmeyen .NET Framework'e başvuruyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index ecba308627fb1..e14aebea2fc44 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1602,6 +1602,11 @@ Using 指令在以前显示为全局使用 + + array access + array access + + async method builder override 异步方法生成器替代 @@ -1767,6 +1772,11 @@ 与常量字符串上的 ReadOnly/Span<char> 匹配的模式 + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 包含类型“{1}”的程序集“{0}”引用了 .NET Framework,而此操作不受支持。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 91e7555f8d6ee..0fcfdca7b253e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1602,6 +1602,11 @@ Using 指示詞先前顯示為全域 using + + array access + array access + + async method builder override 非同步方法建立器覆寫 @@ -1767,6 +1772,11 @@ pattern matching ReadOnly/Span<char> on constant string + + pointer element access + pointer element access + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 包含類型 '{1}' 的組件 '{0}' 參考了 .NET Framework,此情形不受支援。 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/ObjectAndCollectionInitializerTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/ObjectAndCollectionInitializerTests.cs index ec99d6dd3ae82..8d9d8a27aea00 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/ObjectAndCollectionInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/ObjectAndCollectionInitializerTests.cs @@ -1311,7 +1311,6 @@ static void Main() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [Fact] public void DictionaryInitializerTestSideeffects001a() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 56aa2d491abbd..b1fb16a62054e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -9707,15 +9707,12 @@ public static void Main() // (9,9): error CS8147: Properties which return by reference cannot have set accessors // set { } Diagnostic(ErrorCode.ERR_RefPropertyCannotHaveSetAccessor, "set").WithLocation(9, 9), - // (15,32): error CS1525: Invalid expression term 'ref' + // (15,28): error CS8373: The left-hand side of a ref assignment must be a ref variable. // var c = new C(0) { X = ref a[0] }; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref a[0]").WithArguments("ref").WithLocation(15, 32), - // (15,32): error CS1073: Unexpected token 'ref' - // var c = new C(0) { X = ref a[0] }; - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(15, 32), - // (17,26): error CS1073: Unexpected token 'ref' + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "X").WithLocation(15, 28), + // (17,22): error CS8373: The left-hand side of a ref assignment must be a ref variable. // c = c with { X = ref a[0] }; - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(17, 26) + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "X").WithLocation(17, 22) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 5eb42cd47025c..dfe6c6eba8e68 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -11743,5 +11743,1566 @@ public void ReturnThis_02(LanguageVersion languageVersion) // ref readonly R F3() => ref this; Diagnostic(ErrorCode.ERR_RefReturnStructThis, "this").WithLocation(5, 32)); } + + [Fact] + public void RefInitializer_LangVer() + { + var source = @" +int x = 42; +var r = new R() { field = ref x }; + +ref struct R +{ + public ref int field; +} +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics( + // (7,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // public ref int field; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref int").WithArguments("ref fields", "11.0").WithLocation(7, 12) + ); + + comp = CreateCompilation(source, parseOptions: TestOptions.Regular11); + comp.VerifyDiagnostics(); + } + + [Fact] + public void RefInitializer_LangVer_FromMetadata() + { + var lib_cs = @" +public ref struct R +{ + public ref int field; +} +"; + var source = @" +int x = 42; +var r1 = new R() { field = ref x }; // 1 +var r2 = new R() { field = x }; // 2 + +R r3 = default; +_ = r3 with { field = ref x }; // 3 +"; + var lib = CreateCompilation(lib_cs, parseOptions: TestOptions.Regular11); + + var comp = CreateCompilation(source, references: new[] { lib.EmitToImageReference() }, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics( + // (3,20): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // var r1 = new R() { field = ref x }; // 1 + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "field").WithArguments("ref fields", "11.0").WithLocation(3, 20), + // (4,20): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // var r2 = new R() { field = x }; // 2 + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "field").WithArguments("ref fields", "11.0").WithLocation(4, 20), + // (7,15): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. + // _ = r3 with { field = ref x }; // 3 + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "field").WithArguments("ref fields", "11.0").WithLocation(7, 15) + ); + } + + [Fact] + public void RefInitializer() + { + var source = @" +public class C +{ + public static void Main() + { + int x = 42; + var r = new R() { field = ref x }; + System.Console.Write(r.ToString()); + } +} + +ref struct R +{ + public ref int field; + public override string ToString() + { + return field.ToString(); + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("42")); + verifier.VerifyIL("C.Main", +""" +{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (int V_0, //x + R V_1, //r + R V_2) + IL_0000: ldc.i4.s 42 + IL_0002: stloc.0 + IL_0003: ldloca.s V_2 + IL_0005: initobj "R" + IL_000b: ldloc.2 + IL_000c: ldfld "ref int R.field" + IL_0011: ldloc.0 + IL_0012: stind.i4 + IL_0013: ldloc.2 + IL_0014: stloc.1 + IL_0015: ldloca.s V_1 + IL_0017: constrained. "R" + IL_001d: callvirt "string object.ToString()" + IL_0022: call "void System.Console.Write(string)" + IL_0027: ret +} +"""); + } + + [Fact] + public void RefInitializer_RHSMustBeDefinitelyAssigned() + { + // The right operand must be definitely assigned at the point of the ref assignment. + var source = @" +int x; +var r = new R() { field = ref x }; + +ref struct R +{ + public ref int field; + public override string ToString() + { + return field.ToString(); + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,31): error CS0165: Use of unassigned local variable 'x' + // var r = new R() { field = ref x }; + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(3, 31) + ); + } + + [Fact] + public void RefInitializer_RHSTypeMustMatch() + { + // The right operand must be an expression that yields an lvalue designating a value of the same type as the left operand. + var source = @" +object x = null; +var r = new R() { field = ref x }; + +ref struct R +{ + public ref int field; + public override string ToString() + { + return field.ToString(); + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,31): error CS8173: The expression must be of type 'int' because it is being assigned by reference + // var r = new R() { field = ref x }; + Diagnostic(ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion, "x").WithArguments("int").WithLocation(3, 31) + ); + } + + [Fact] + public void RefInitializer_RHSTypeMustMatch_ImplicitConversionExists() + { + // The right operand must be an expression that yields an lvalue designating a value of the same type as the left operand. + var source = @" +S1 x = default; +var r = new R() { field = ref x }; + +struct S1 { } +struct S2 +{ + public static implicit operator S2(S1 s1) => throw null; +} +ref struct R +{ + public ref S2 field; + public override string ToString() + { + return field.ToString(); + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,31): error CS8173: The expression must be of type 'S2' because it is being assigned by reference + // var r = new R() { field = ref x }; + Diagnostic(ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion, "x").WithArguments("S2").WithLocation(3, 31) + ); + } + + [Fact] + public void RefInitializer_StaticRefField() + { + var source = @" +int x = 0; +var r = new R() { field = ref x }; + +ref struct R +{ + public static ref int field; + public override string ToString() + { + return field.ToString(); + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,19): error CS1914: Static field or property 'R.field' cannot be assigned in an object initializer + // var r = new R() { field = ref x }; + Diagnostic(ErrorCode.ERR_StaticMemberInObjectInitializer, "field").WithArguments("R.field").WithLocation(3, 19), + // (7,27): error CS0106: The modifier 'static' is not valid for this item + // public static ref int field; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "field").WithArguments("static").WithLocation(7, 27) + ); + } + + [Fact] + public void RefInitializer_VarInvocationReserved() + { + var source = @" +var r = new R() { field = ref var() }; + +ref struct R +{ + public ref int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (2,31): error CS8199: The syntax 'var (...)' as an lvalue is reserved. + // var r = new R() { field = ref var() }; + Diagnostic(ErrorCode.ERR_VarInvocationLvalueReserved, "var()").WithLocation(2, 31), + // (2,31): error CS0103: The name 'var' does not exist in the current context + // var r = new R() { field = ref var() }; + Diagnostic(ErrorCode.ERR_NameNotInContext, "var").WithArguments("var").WithLocation(2, 31) + ); + } + + [Fact] + public void RefInitializer_ReadonlyRefField() + { + var source = @" +int x = 42; +var r = new R() { field = ref x }; + +ref struct R +{ + public readonly ref int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,19): 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) + // var r = new R() { field = ref x }; + Diagnostic(ErrorCode.ERR_AssgReadonly, "field").WithLocation(3, 19) + ); + } + + [Fact] + public void RefInitializer_RefReadonlyField() + { + var source = @" +int x = 42; +var r = new R() { field = ref x }; + +ref struct R +{ + public ref readonly int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void RefInitializer_RefReadonlyValue_Field() + { + // If the left operand is a writeable ref (i.e. it designates anything other than a `ref readonly` local or `in` parameter), then the right operand must be a writeable lvalue. + var source = @" +class C +{ + ref readonly int Value() => throw null; + + void M() + { + var r = new R() { field = ref Value() }; + } +} + +ref struct R +{ + public ref int field; +} +"; + // Confusing error message + // Tracked by https://github.com/dotnet/roslyn/issues/62756 + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,39): error CS8331: Cannot assign to method 'C.Value()' because it is a readonly variable + // var r = new R() { field = ref Value() }; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "Value()").WithArguments("method", "C.Value()").WithLocation(8, 39) + ); + } + + [Fact] + public void RefInitializer_AssignInParameter() + { + var source = @" +class C +{ + static void F(in int i) + { + _ = new R1 { _f = ref i }; + _ = new R2 { _f = ref i }; // 1 + } +} + +ref struct R1 +{ + public ref readonly int _f; +} + +ref struct R2 +{ + public ref int _f; +} +"; + // Diagnostic is missing parameter name + // Tracked by https://github.com/dotnet/roslyn/issues/62096 + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,31): error CS8331: Cannot assign to variable 'in int' because it is a readonly variable + // _ = new R2 { _f = ref i }; // 1 + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "i").WithArguments("variable", "in int").WithLocation(7, 31) + ); + } + + [Fact] + public void RefInitializer_RefReadonlyValue_GetOnlyProperty() + { + var source = @" +class C +{ + ref readonly int Value() => throw null; + + void M() + { + var r = new R() { Property = ref Value() }; + } +} + +ref struct R +{ + public ref int Property { get => throw null; } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,27): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new R() { Property = ref Value() }; + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "Property").WithLocation(8, 27) + ); + } + + [Fact] + public void RefInitializer_RefReadonlyValue_Property() + { + var source = @" +class C +{ + ref readonly int Value() => throw null; + + void M() + { + var r = new R() { Property = ref Value() }; + } +} + +ref struct R +{ + public ref int Property { get => throw null; set => throw null; } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,27): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new R() { Property = ref Value() }; + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "Property").WithLocation(8, 27), + // (14,50): error CS8147: Properties which return by reference cannot have set accessors + // public ref int Property { get => throw null; set => throw null; } + Diagnostic(ErrorCode.ERR_RefPropertyCannotHaveSetAccessor, "set").WithLocation(14, 50) + ); + } + + [Fact] + public void RefInitializer_RefReadonlyValue_Indexer() + { + var source = @" +var r = new R() { [0] = ref Value() }; // 1 + +R r2 = default; +r2[0] = ref Value(); // 2 + +ref readonly int Value() => throw null; + +ref struct R +{ + public ref int this[int i] { get => throw null; } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (2,19): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new R() { [0] = ref Value() }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "[0]").WithLocation(2, 19), + // (5,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // r2[0] = ref Value(); // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "r2[0]").WithLocation(5, 1) + ); + } + + [Fact] + public void RefInitializer_Indexer() + { + var source = @" +var r = new R() { [0] = ref Value() }; // 1 + +R r2 = default; +r2[0] = ref Value(); // 2 + +ref int Value() => throw null; + +ref struct R +{ + public ref int this[int i] { get => throw null; } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (2,19): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new R() { [0] = ref Value() }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "[0]").WithLocation(2, 19), + // (5,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // r2[0] = ref Value(); // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "r2[0]").WithLocation(5, 1) + ); + } + + [Fact] + public void RefInitializer_ValueMustReferToLocation() + { + var source = @" +class C +{ + int Value() => throw null; + + void M() + { + var r = new R() { field = ref Value() }; + } +} + +ref struct R +{ + public ref int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,39): error CS1510: A ref or out value must be an assignable variable + // var r = new R() { field = ref Value() }; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "Value()").WithLocation(8, 39) + ); + } + + [Fact] + public void RefInitializer_RefReadonlyField_RefReadonlyValue() + { + var source = @" +class C +{ + ref readonly int Value() => throw null; + + void M() + { + var r = new R() { field = ref Value() }; + } +} + +ref struct R +{ + public ref readonly int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void RefInitializer_OnInterface_Field() + { + var source = @" +class C +{ + void M() where T : I, new() + { + int x = 42; + var t = new T() { field = ref x }; + } +} +interface I +{ + public ref int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,20): error CS0525: Interfaces cannot contain instance fields + // public ref int field; + Diagnostic(ErrorCode.ERR_InterfacesCantContainFields, "field").WithLocation(12, 20), + // (12,20): error CS9059: A ref field can only be declared in a ref struct. + // public ref int field; + Diagnostic(ErrorCode.ERR_RefFieldInNonRefStruct, "field").WithLocation(12, 20) + ); + } + + [Fact] + public void RefInitializer_OnInterface_Property() + { + var source = @" +class C +{ + void M() where T : I, new() + { + int x = 42; + var t = new T() { Property = ref x }; + } +} + +interface I +{ + public ref int Property { get; } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,27): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var t = new T() { Property = ref x }; + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "Property").WithLocation(7, 27) + ); + } + + [Fact] + public void RefInitializer_Collection() + { + var source = @" +using System.Collections; + +int x = 42; +int y = 43; +var r = new R() { ref x, ref y }; + +struct R : IEnumerable +{ + public void Add(ref int x) => throw null; + public IEnumerator GetEnumerator() => throw null; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,5): warning CS0219: The variable 'x' is assigned but its value is never used + // int x = 42; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(4, 5), + // (5,5): warning CS0219: The variable 'y' is assigned but its value is never used + // int y = 43; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y").WithArguments("y").WithLocation(5, 5), + // (6,19): error CS1073: Unexpected token 'ref' + // var r = new R() { ref x, ref y }; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(6, 19), + // (6,26): error CS1073: Unexpected token 'ref' + // var r = new R() { ref x, ref y }; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(6, 26) + ); + } + + [Fact] + public void RefInitializer_OnNonRefField() + { + var source = @" +int x = 42; +var r = new R() { field = ref x }; // 1 + +R r2 = default; +r2.field = ref x; // 2 + +ref struct R +{ + public int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,19): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new R() { field = ref x }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "field").WithLocation(3, 19), + // (6,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // r2.field = ref x; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "r2.field").WithLocation(6, 1) + ); + } + + [Fact] + public void RefInitializer_OnNonRefProperty() + { + var source = @" +int x = 42; +var r = new R() { Property = ref x }; // 1 + +R r2 = default; +r2.Property = ref x; // 2 + +ref struct R +{ + public int Property { get; set; } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,19): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new R() { Property = ref x }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "Property").WithLocation(3, 19), + // (6,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // r2.Property = ref x; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "r2.Property").WithLocation(6, 1) + ); + } + + [Fact] + public void RefInitializer_OnNonRefIndexer() + { + var source = @" +int x = 42; +var r = new R() { [0] = ref x }; // 1 + +R r2 = default; +r2[0] = ref x; // 2 + +ref struct R +{ + public int this[int i] { get => throw null; set => throw null; } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,19): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new R() { [0] = ref x }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "[0]").WithLocation(3, 19), + // (6,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // r2[0] = ref x; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "r2[0]").WithLocation(6, 1) + ); + } + + [Fact] + public void RefInitializer_OnArray() + { + var source = @" +public ref struct C +{ + C M() + { + int x = 0; + var c = new C { array = { [0] = ref x } }; // 1 + return c; + } + + void M2() + { + int x = 0; + C c2 = new C(); + c2.array[0] = ref x; // 2 + } + + public int[] array; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,35): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var c = new C { array = { [0] = ref x } }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "[0]").WithLocation(7, 35), + // (15,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // c2.array[0] = ref x; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "c2.array[0]").WithLocation(15, 9) + ); + } + + [Fact] + public void RefInitializer_OnPointer() + { + var source = @" +public unsafe class C +{ + public int* pointer; + + C M() + { + int x = 0; + var c = new C { pointer = { [0] = ref x } }; // 1 + return c; + } + + void M2() + { + int x = 0; + C c2 = new C(); + c2.pointer[0] = ref x; // 2 + } +} +"; + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); + comp.VerifyDiagnostics( + // (9,37): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var c = new C { pointer = { [0] = ref x } }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "[0]").WithLocation(9, 37), + // (17,9): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // c2.pointer[0] = ref x; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "c2.pointer[0]").WithLocation(17, 9) + ); + } + + [Fact] + public void RefInitializer_OnEvent() + { + var source = @" +int x = 42; +var r = new C { a = ref x }; // 1 + +C c = default; +c.a = ref x; // 2 + +class C +{ + public event System.Action a; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,17): error CS0070: The event 'C.a' can only appear on the left hand side of += or -= (except when used from within the type 'C') + // var r = new C { a = ref x }; // 1 + Diagnostic(ErrorCode.ERR_BadEventUsage, "a").WithArguments("C.a", "C").WithLocation(3, 17), + // (6,3): error CS0070: The event 'C.a' can only appear on the left hand side of += or -= (except when used from within the type 'C') + // c.a = ref x; // 2 + Diagnostic(ErrorCode.ERR_BadEventUsage, "a").WithArguments("C.a", "C").WithLocation(6, 3) + ); + } + + [Fact] + public void RefInitializer_OnEvent_ThisMemberAccess() + { + var source = @" +int x = 42; +var r1 = new C { this.a = ref x }; // 1 + +class C +{ + public event System.Action a; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,16): error CS1922: Cannot initialize type 'C' with a collection initializer because it does not implement 'System.Collections.IEnumerable' + // var r1 = new C { this.a = ref x }; // 1 + Diagnostic(ErrorCode.ERR_CollectionInitRequiresIEnumerable, "{ this.a = ref x }").WithArguments("C").WithLocation(3, 16), + // (3,18): error CS0747: Invalid initializer member declarator + // var r1 = new C { this.a = ref x }; // 1 + Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, "this.a = ref x").WithLocation(3, 18), + // (3,18): error CS0026: Keyword 'this' is not valid in a static property, static method, or static field initializer + // var r1 = new C { this.a = ref x }; // 1 + Diagnostic(ErrorCode.ERR_ThisInStaticMeth, "this").WithLocation(3, 18), + // (7,32): warning CS0067: The event 'C.a' is never used + // public event System.Action a; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "a").WithArguments("C.a").WithLocation(7, 32) + ); + } + + [Fact] + public void RefInitializer_OnMethodGroup() + { + var source = @" +int x = 42; +var r = new C { F = ref x }; // 1 + +C c = default; +c.F = ref x; // 2 + +class C +{ + public void F() { } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,17): error CS1913: Member 'F' cannot be initialized. It is not a field or property. + // var r = new C { F = ref x }; // 1 + Diagnostic(ErrorCode.ERR_MemberCannotBeInitialized, "F").WithArguments("F").WithLocation(3, 17), + // (6,1): error CS1656: Cannot assign to 'F' because it is a 'method group' + // c.F = ref x; // 2 + Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "c.F").WithArguments("F", "method group").WithLocation(6, 1) + ); + } + + [Fact] + public void RefInitializer_Nested() + { + var source = @" +class C +{ + void M() + { + int x = 42; + var r = new Container { item = { field = ref x } }; + System.Console.Write(r.item.field); + } +} +ref struct Container +{ + public Item item; +} +ref struct Item +{ + public ref int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("42")); + verifier.VerifyIL("C.M", @" +{ + // Code size 42 (0x2a) + .maxstack 2 + .locals init (int V_0, //x + Container V_1) + IL_0000: ldc.i4.s 42 + IL_0002: stloc.0 + IL_0003: ldloca.s V_1 + IL_0005: initobj ""Container"" + IL_000b: ldloc.1 + IL_000c: ldfld ""Item Container.item"" + IL_0011: ldfld ""ref int Item.field"" + IL_0016: ldloc.0 + IL_0017: stind.i4 + IL_0018: ldloc.1 + IL_0019: ldfld ""Item Container.item"" + IL_001e: ldfld ""ref int Item.field"" + IL_0023: ldind.i4 + IL_0024: call ""void System.Console.Write(int)"" + IL_0029: ret +} +"); + } + + [Fact] + public void RefInitializer_Nullability() + { + var source = @" +#nullable enable + +S x1 = default; +S x2 = default; + +_ = new R { field = ref x1 }; +_ = new R { field = ref x2 }; // 1 +_ = new R { field = ref x1 }; // 2 +_ = new R { field = ref x2 }; + +_ = new R() with { field = ref x1 }; +_ = new R() with { field = ref x2 }; // 3 +_ = new R() with { field = ref x1 }; // 4 +_ = new R() with { field = ref x2 }; + +struct S { } +ref struct R +{ + public ref S field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,33): warning CS8619: Nullability of reference types in value of type 'S' doesn't match target type 'S'. + // _ = new R { field = ref x2 }; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2").WithArguments("S", "S").WithLocation(8, 33), + // (9,34): warning CS8619: Nullability of reference types in value of type 'S' doesn't match target type 'S'. + // _ = new R { field = ref x1 }; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x1").WithArguments("S", "S").WithLocation(9, 34), + // (13,40): warning CS8619: Nullability of reference types in value of type 'S' doesn't match target type 'S'. + // _ = new R() with { field = ref x2 }; // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2").WithArguments("S", "S").WithLocation(13, 40), + // (14,41): warning CS8619: Nullability of reference types in value of type 'S' doesn't match target type 'S'. + // _ = new R() with { field = ref x1 }; // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x1").WithArguments("S", "S").WithLocation(14, 41) + ); + } + + [Fact] + public void RefInitializer_RefOnNestedInitializer() + { + var source = @" +int x = 42; +var r = new R { field = ref { item = 42 } }; // 1 + +struct S +{ + public int item; +} +ref struct R +{ + public ref S field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (2,5): warning CS0219: The variable 'x' is assigned but its value is never used + // int x = 42; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(2, 5), + // (3,29): error CS1525: Invalid expression term '{' + // var r = new R { field = ref { item = 42 } }; // 1 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "{").WithArguments("{").WithLocation(3, 29), + // (3,29): error CS1003: Syntax error, ',' expected + // var r = new R { field = ref { item = 42 } }; // 1 + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",").WithLocation(3, 29), + // (3,29): error CS0747: Invalid initializer member declarator + // var r = new R { field = ref { item = 42 } }; // 1 + Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, "{ item = 42 }").WithLocation(3, 29), + // (3,31): error CS0103: The name 'item' does not exist in the current context + // var r = new R { field = ref { item = 42 } }; // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "item").WithArguments("item").WithLocation(3, 31) + ); + } + + [Fact] + public void RefInitializer_FieldOnDynamic() + { + var source = @" +int x = 42; +var r = new S { D = { field = ref x } }; // 1 + +S s = default; +s.D.field = ref x; // 2 + +struct S +{ + public dynamic D; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,23): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new S { D = { field = ref x } }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "field").WithLocation(3, 23), + // (6,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // s.D.field = ref x; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.D.field").WithLocation(6, 1) + ); + } + + [Fact] + public void RefInitializer_DynamicField() + { + var source = @" +int i = 42; +var r = new R { F = ref i }; // 1 + +ref struct R +{ + public ref T F; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,34): error CS8173: The expression must be of type 'dynamic' because it is being assigned by reference + // var r = new R { F = ref i }; // 1 + Diagnostic(ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion, "i").WithArguments("dynamic").WithLocation(3, 34) + ); + } + + [Fact] + public void RefInitializer_DynamicField_DynamicValue() + { + var source = @" +public class C +{ + public static void Main() + { + dynamic i = 42; + var r = new R { F = ref i }; + System.Console.Write(r.F); + + var r2 = new R(ref i); + System.Console.Write(r2.F); + } +} + +ref struct R +{ + public R(ref T f) { F = ref f; } + public ref T F; +} +"; + var comp = CreateCompilation(source, options: TestOptions.DebugExe, targetFramework: TargetFramework.NetCoreAppAndCSharp); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("4242")); + verifier.VerifyIL("C.Main", """ +{ + // Code size 258 (0x102) + .maxstack 9 + .locals init (object V_0, //i + R V_1, //r + R V_2, //r2 + R V_3) + IL_0000: nop + IL_0001: ldc.i4.s 42 + IL_0003: box "int" + IL_0008: stloc.0 + IL_0009: ldloca.s V_3 + IL_000b: initobj "R" + IL_0011: ldloc.3 + IL_0012: ldfld "ref dynamic R.F" + IL_0017: ldloc.0 + IL_0018: stind.ref + IL_0019: ldloc.3 + IL_001a: stloc.1 + IL_001b: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0" + IL_0020: brfalse.s IL_0024 + IL_0022: br.s IL_0064 + IL_0024: ldc.i4 0x100 + IL_0029: ldstr "Write" + IL_002e: ldnull + IL_002f: ldtoken "C" + IL_0034: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_0039: ldc.i4.2 + IL_003a: ldc.i4.0 + IL_003b: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] System.GC.AllocateUninitializedArray(int, bool)" + IL_0040: dup + IL_0041: ldc.i4.0 + IL_0042: ldc.i4.s 33 + IL_0044: ldnull + IL_0045: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" + IL_004a: stelem.ref + IL_004b: dup + IL_004c: ldc.i4.1 + IL_004d: ldc.i4.0 + IL_004e: ldnull + IL_004f: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" + IL_0054: stelem.ref + IL_0055: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable, System.Type, System.Collections.Generic.IEnumerable)" + IL_005a: call "System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)" + IL_005f: stsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0" + IL_0064: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0" + IL_0069: ldfld "System.Action System.Runtime.CompilerServices.CallSite>.Target" + IL_006e: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0" + IL_0073: ldtoken "System.Console" + IL_0078: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_007d: ldloc.1 + IL_007e: ldfld "ref dynamic R.F" + IL_0083: ldind.ref + IL_0084: callvirt "void System.Action.Invoke(System.Runtime.CompilerServices.CallSite, System.Type, dynamic)" + IL_0089: nop + IL_008a: ldloca.s V_0 + IL_008c: newobj "R..ctor(ref dynamic)" + IL_0091: stloc.2 + IL_0092: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__1" + IL_0097: brfalse.s IL_009b + IL_0099: br.s IL_00db + IL_009b: ldc.i4 0x100 + IL_00a0: ldstr "Write" + IL_00a5: ldnull + IL_00a6: ldtoken "C" + IL_00ab: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_00b0: ldc.i4.2 + IL_00b1: ldc.i4.0 + IL_00b2: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] System.GC.AllocateUninitializedArray(int, bool)" + IL_00b7: dup + IL_00b8: ldc.i4.0 + IL_00b9: ldc.i4.s 33 + IL_00bb: ldnull + IL_00bc: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" + IL_00c1: stelem.ref + IL_00c2: dup + IL_00c3: ldc.i4.1 + IL_00c4: ldc.i4.0 + IL_00c5: ldnull + IL_00c6: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" + IL_00cb: stelem.ref + IL_00cc: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable, System.Type, System.Collections.Generic.IEnumerable)" + IL_00d1: call "System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)" + IL_00d6: stsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__1" + IL_00db: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__1" + IL_00e0: ldfld "System.Action System.Runtime.CompilerServices.CallSite>.Target" + IL_00e5: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__1" + IL_00ea: ldtoken "System.Console" + IL_00ef: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_00f4: ldloc.2 + IL_00f5: ldfld "ref dynamic R.F" + IL_00fa: ldind.ref + IL_00fb: callvirt "void System.Action.Invoke(System.Runtime.CompilerServices.CallSite, System.Type, dynamic)" + IL_0100: nop + IL_0101: ret +} +"""); + } + + [Fact] + public void RefInitializer_SubstitutedObjectField() + { + var source = @" +public class C +{ + public static void Main() + { + object i = 42; + var r = new R { F = ref i }; + System.Console.Write(r.F); + + var r2 = new R(ref i); + System.Console.Write(r2.F); + } +} + +ref struct R +{ + public R(ref T f) { F = ref f; } + public ref T F; +} +"; + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("4242")); + verifier.VerifyIL("C.Main", """ +{ + // Code size 55 (0x37) + .maxstack 2 + .locals init (object V_0, //i + R V_1) + IL_0000: ldc.i4.s 42 + IL_0002: box "int" + IL_0007: stloc.0 + IL_0008: ldloca.s V_1 + IL_000a: initobj "R" + IL_0010: ldloc.1 + IL_0011: ldfld "ref object R.F" + IL_0016: ldloc.0 + IL_0017: stind.ref + IL_0018: ldloc.1 + IL_0019: ldfld "ref object R.F" + IL_001e: ldind.ref + IL_001f: call "void System.Console.Write(object)" + IL_0024: ldloca.s V_0 + IL_0026: newobj "R..ctor(ref object)" + IL_002b: ldfld "ref object R.F" + IL_0030: ldind.ref + IL_0031: call "void System.Console.Write(object)" + IL_0036: ret +} +"""); + } + + [Fact] + public void RefInitializer_DynamicInstance() + { + var source = @" +int x = 42; +var r = new dynamic { field = ref x }; // 1 + +dynamic r2 = null; +r2.field = ref x; // 2 +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,13): error CS8386: Invalid object creation + // var r = new dynamic { field = ref x }; // 1 + Diagnostic(ErrorCode.ERR_InvalidObjectCreation, "dynamic").WithLocation(3, 13), + // (3,23): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new dynamic { field = ref x }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "field").WithLocation(3, 23), + // (6,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // r2.field = ref x; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "r2.field").WithLocation(6, 1) + ); + } + + [Fact] + public void RefInitializer_DynamicIndexer() + { + var source = @" +int x = 42; +var r = new S { D = { [0] = ref x } }; // 1 + +S s = default; +s.D[0] = ref x; // 2 + +struct S +{ + public dynamic D; +} +ref struct R +{ +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,23): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var r = new S { D = { [0] = ref x } }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "[0]").WithLocation(3, 23), + // (6,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // s.D[0] = ref x; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "s.D[0]").WithLocation(6, 1) + ); + } + + [Fact] + public void RefInitializer_DynamicIndexer_Nested() + { + var source = @" +dynamic x = 1; +int i = 42; +var a = new A() { [y: x, x: x] = { X = ref i } }; // 1 + +A a2 = null; +a2[y: x, x: x].X = ref i; // 2 + +public class A +{ + public dynamic this[int x, int y] { get => throw null; } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,36): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // var a = new A() { [y: x, x: x] = { X = ref i } }; // 1 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "X").WithLocation(4, 36), + // (7,1): error CS8373: The left-hand side of a ref assignment must be a ref variable. + // a2[y: x, x: x].X = ref i; // 2 + Diagnostic(ErrorCode.ERR_RefLocalOrParamExpected, "a2[y: x, x: x].X").WithLocation(7, 1) + ); + } + + [Fact] + public void RefInitializer_Escape() + { + var source = @" +public class C +{ + public static R M1() + { + int x = 42; + var r = new R { field = ref x }; + return r; // 1 + } + public static R M2(ref int x) + { + var r = new R { field = ref x }; + return r; + } + public static R M3() + { + R r = default; + { + int x = 42; + r = new R { field = ref x }; // 2 + } + return r; + } + public static R M4() + { + R r = default; + int x = 42; + { + r = new R { field = ref x }; // 3 + } + return r; + } + public static void M5(ref R r) + { + int x = 42; + r = new R { field = ref x }; // 4 + } +} + +public ref struct R +{ + public ref int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope + // return r; // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(8, 16), + // (20,25): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R { field = ref x }; // 2 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "field = ref x").WithArguments("x").WithLocation(20, 25), + // (29,25): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R { field = ref x }; // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "field = ref x").WithArguments("x").WithLocation(29, 25), + // (36,21): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R { field = ref x }; // 4 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "field = ref x").WithArguments("x").WithLocation(36, 21) + ); + + // Initializer values behave like constructor parameters for purpose of escape analysis + source = @" +public class C +{ + public static R M1() + { + int x = 42; + var r = new R(ref x); + return r; // 1 + } + public static R M2(ref int x) + { + var r = new R(ref x); + return r; + } + public static R M3() + { + R r = default; + { + int x = 42; + r = new R(ref x); // 2 + } + return r; + } + public static R M4() + { + R r = default; + int x = 42; + { + r = new R(ref x); // 3 + } + return r; + } + public static void M5(ref R r) + { + int x = 42; + r = new R(ref x); // 4 + } +} + +public ref struct R +{ + public ref int field; + public R(ref int i) { } +} +"; + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope + // return r; // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(8, 16), + // (20,17): error CS8347: Cannot use a result of 'R.R(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // r = new R(ref x); // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(ref x)").WithArguments("R.R(ref int)", "i").WithLocation(20, 17), + // (20,27): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R(ref x); // 2 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(20, 27), + // (29,17): error CS8347: Cannot use a result of 'R.R(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // r = new R(ref x); // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(ref x)").WithArguments("R.R(ref int)", "i").WithLocation(29, 17), + // (29,27): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R(ref x); // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(29, 27), + // (36,13): error CS8347: Cannot use a result of 'R.R(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // r = new R(ref x); // 4 + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(ref x)").WithArguments("R.R(ref int)", "i").WithLocation(36, 13), + // (36,23): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R(ref x); // 4 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "x").WithArguments("x").WithLocation(36, 23) + ); + } + + [Fact] + public void RefInitializer_Escape_Nested() + { + var source = @" +class C +{ + public static Container M3() + { + Container r = default; + { + int x = 42; + var r = new Container { item = { field = ref x } }; // 1 + } + return r; + } + public static Container M4() + { + Container r = default; + int x = 42; + { + r = new Container { item = { field = ref x } }; // 2 + } + return r; + } + public static void M5(ref Container r) + { + int x = 42; + r = new Container { item = { field = ref x } }; // 3 + } + public static Container M6() + { + int x = 42; + var r = new Container { item = { field = ref x } }; + return r; // 4 + } + } +ref struct Container +{ + public Item item; +} +ref struct Item +{ + public ref int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (9,17): error CS0136: A local or parameter named 'r' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // var r = new Container { item = { field = ref x } }; // 1 + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "r").WithArguments("r").WithLocation(9, 17), + // (18,42): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new Container { item = { field = ref x } }; // 2 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "field = ref x").WithArguments("x").WithLocation(18, 42), + // (25,38): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new Container { item = { field = ref x } }; // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "field = ref x").WithArguments("x").WithLocation(25, 38), + // (31,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope + // return r; // 4 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(31, 16) + ); + } + + [Fact] + public void RefWith() + { + var source = @" +public class C +{ + public static void Main() + { + int x = 42; + var r = new R() with { field = ref x }; + System.Console.Write(r.ToString()); + } +} + +ref struct R +{ + public ref int field; + public override string ToString() + { + return field.ToString(); + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(source, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("42")); + verifier.VerifyIL("C.Main", +""" +{ + // Code size 40 (0x28) + .maxstack 2 + .locals init (int V_0, //x + R V_1, //r + R V_2) + IL_0000: ldc.i4.s 42 + IL_0002: stloc.0 + IL_0003: ldloca.s V_2 + IL_0005: initobj "R" + IL_000b: ldloc.2 + IL_000c: ldfld "ref int R.field" + IL_0011: ldloc.0 + IL_0012: stind.i4 + IL_0013: ldloc.2 + IL_0014: stloc.1 + IL_0015: ldloca.s V_1 + IL_0017: constrained. "R" + IL_001d: callvirt "string object.ToString()" + IL_0022: call "void System.Console.Write(string)" + IL_0027: ret +} +"""); + } + + [Fact] + public void RefWith_Escape() + { + var source = @" +public class C +{ + public static R M1() + { + int x = 42; + var r = new R() with { field = ref x }; + return r; // 1 + } + public static R M2(ref int x) + { + var r = new R() with { field = ref x }; + return r; + } + public static R M3() + { + R r = default; + { + int x = 42; + r = new R() with { field = ref x }; // 2 + } + return r; + } + public static R M4() + { + R r = default; + int x = 42; + { + r = new R() with { field = ref x }; // 3 + } + return r; + } + public static void M5(ref R r) + { + int x = 42; + r = new R() with { field = ref x }; // 4 + } +} + +public ref struct R +{ + public ref int field; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,16): error CS8352: Cannot use variable 'r' in this context because it may expose referenced variables outside of their declaration scope + // return r; // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "r").WithArguments("r").WithLocation(8, 16), + // (20,32): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R() with { field = ref x }; // 2 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "field = ref x").WithArguments("x").WithLocation(20, 32), + // (29,32): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R() with { field = ref x }; // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "field = ref x").WithArguments("x").WithLocation(29, 32), + // (36,28): error CS8168: Cannot return local 'x' by reference because it is not a ref local + // r = new R() with { field = ref x }; // 4 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "field = ref x").WithArguments("x").WithLocation(36, 28) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs index 4134f1b02832e..cce5b664d489c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs @@ -5673,9 +5673,6 @@ public void AnonymousObjectCreation_BadRef() public void ObjectInitializer_BadRef() { UsingExpression("new C { P = ref }", - // (1,13): error CS1525: Invalid expression term 'ref' - // new C { P = ref } - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref ").WithArguments("ref").WithLocation(1, 13), // (1,17): error CS1525: Invalid expression term '}' // new C { P = ref } Diagnostic(ErrorCode.ERR_InvalidExprTerm, "}").WithArguments("}").WithLocation(1, 17)); @@ -5717,9 +5714,6 @@ public void ObjectInitializer_BadRef() public void CollectionInitializer_BadRef_01() { UsingExpression("new C { ref }", - // (1,9): error CS1525: Invalid expression term 'ref' - // new C { ref } - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref ").WithArguments("ref").WithLocation(1, 9), // (1,13): error CS1525: Invalid expression term '}' // new C { ref } Diagnostic(ErrorCode.ERR_InvalidExprTerm, "}").WithArguments("}").WithLocation(1, 13)); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs index e83f7be98b224..395ee4bbf2c31 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefFieldParsingTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -414,17 +415,104 @@ public void ReadOnlyRefParameter(LanguageVersion languageVersion) EOF(); } - // https://github.com/dotnet/roslyn/issues/62120: Support ref field assignment in object initializers. - [Theory] + [Theory, WorkItem(62120, "https://github.com/dotnet/roslyn/issues/62120")] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] public void ObjectInitializer(LanguageVersion languageVersion) { string source = "new S { F = ref t }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion)); + + 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(); + } + + [Theory, WorkItem(62120, "https://github.com/dotnet/roslyn/issues/62120")] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void ObjectInitializer_CompoundAssignment(LanguageVersion languageVersion) + { + string source = "new S { F += ref t }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,14): error CS1525: Invalid expression term 'ref' + // new S { F += ref t } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref t").WithArguments("ref").WithLocation(1, 14) + ); + + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + N(SyntaxKind.CollectionInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AddAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + N(SyntaxKind.PlusEqualsToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void RefObjectInitializer_NestedInitializer(LanguageVersion languageVersion) + { + string source = "new S { F = ref { F2 = 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)); + // (1,17): error CS1525: Invalid expression term '{' + // new S { F = ref { F2 = t } } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "{").WithArguments("{").WithLocation(1, 17), + // (1,17): error CS1003: Syntax error, ',' expected + // new S { F = ref { F2 = t } } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",").WithLocation(1, 17) + ); N(SyntaxKind.ObjectCreationExpression); { @@ -446,11 +534,166 @@ public void ObjectInitializer(LanguageVersion languageVersion) N(SyntaxKind.RefExpression); { N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ComplexElementInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F2"); + } + N(SyntaxKind.EqualsToken); N(SyntaxKind.IdentifierName); { N(SyntaxKind.IdentifierToken, "t"); } } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void RefCollectionInitializer(LanguageVersion languageVersion) + { + string source = "new S { ref t }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion)); + + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + N(SyntaxKind.CollectionInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void RefDictionaryInitializer(LanguageVersion languageVersion) + { + string source = "new S { [0] = ref t }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion)); + + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.ImplicitElementAccess); + { + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + public void RefComplexElementInitializer(LanguageVersion languageVersion) + { + string source = "new S { ref { 1, 2 } }"; + UsingExpression(source, TestOptions.Regular.WithLanguageVersion(languageVersion), + // (1,13): error CS1525: Invalid expression term '{' + // new S { ref { 1, 2 } } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "{").WithArguments("{").WithLocation(1, 13), + // (1,13): error CS1003: Syntax error, ',' expected + // new S { ref { 1, 2 } } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",").WithLocation(1, 13) + ); + + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + N(SyntaxKind.CollectionInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ComplexElementInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + N(SyntaxKind.CloseBraceToken); } N(SyntaxKind.CloseBraceToken); }