diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 127509552c312..0fa1356951c24 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -720,28 +720,39 @@ private void EmitArgument(BoundExpression argument, RefKind refKind) EmitExpression(argument, true); break; - case RefKind.In: - var temp = EmitAddress(argument, AddressKind.ReadOnly); - AddExpressionTemp(temp); - break; - default: - Debug.Assert(refKind is RefKind.Ref or RefKind.Out or RefKindExtensions.StrictIn); - // NOTE: passing "ReadOnlyStrict" here. - // we should not get an address of a copy if at all possible - var unexpectedTemp = EmitAddress(argument, refKind == RefKindExtensions.StrictIn ? AddressKind.ReadOnlyStrict : AddressKind.Writeable); - if (unexpectedTemp != null) + Debug.Assert(refKind is RefKind.In or RefKind.Ref or RefKind.Out or RefKindExtensions.StrictIn); + var temp = EmitAddress(argument, GetArgumentAddressKind(refKind)); + if (temp != null) { // interestingly enough "ref dynamic" sometimes is passed via a clone // receiver of a ref field can be cloned too - Debug.Assert(argument.Type.IsDynamic() || argument is BoundFieldAccess { FieldSymbol.RefKind: not RefKind.None }, "passing args byref should not clone them into temps"); - AddExpressionTemp(unexpectedTemp); + Debug.Assert(refKind is RefKind.In || argument.Type.IsDynamic() || argument is BoundFieldAccess { FieldSymbol.RefKind: not RefKind.None }, "passing args byref should not clone them into temps"); + AddExpressionTemp(temp); } break; } } + internal static AddressKind GetArgumentAddressKind(RefKind refKind) + { + switch (refKind) + { + case RefKind.None: + throw ExceptionUtilities.UnexpectedValue(refKind); + + case RefKind.In: + return AddressKind.ReadOnly; + + default: + Debug.Assert(refKind is RefKind.Ref or RefKind.Out or RefKindExtensions.StrictIn); + // NOTE: returning "ReadOnlyStrict" here. + // we should not get an address of a copy if at all possible + return refKind == RefKindExtensions.StrictIn ? AddressKind.ReadOnlyStrict : AddressKind.Writeable; + } + } + private void EmitAddressOfExpression(BoundAddressOfOperator expression, bool used) { // NOTE: passing "ReadOnlyStrict" here. diff --git a/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs index c06bd5efc479c..5b909e5a9bbaf 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs @@ -109,13 +109,13 @@ static BoundExpression updateCall( if (receiverRefKind != RefKind.None) { var builder = ArrayBuilder.GetInstance(method.ParameterCount, RefKind.None); - builder[0] = argumentRefKindFromReceiverRefKind(receiverRefKind); + builder[0] = ReceiverArgumentRefKindFromReceiverRefKind(receiverRefKind); argumentRefKinds = builder.ToImmutableAndFree(); } } else { - argumentRefKinds = argumentRefKinds.Insert(0, argumentRefKindFromReceiverRefKind(receiverRefKind)); + argumentRefKinds = argumentRefKinds.Insert(0, ReceiverArgumentRefKindFromReceiverRefKind(receiverRefKind)); } invokedAsExtensionMethod = true; @@ -141,14 +141,14 @@ static BoundExpression updateCall( boundCall.ResultKind, originalMethodsOpt, type); - - static RefKind argumentRefKindFromReceiverRefKind(RefKind receiverRefKind) - { - return SyntheticBoundNodeFactory.ArgumentRefKindFromParameterRefKind(receiverRefKind, useStrictArgumentRefKinds: false); - } } } + public static RefKind ReceiverArgumentRefKindFromReceiverRefKind(RefKind receiverRefKind) + { + return SyntheticBoundNodeFactory.ArgumentRefKindFromParameterRefKind(receiverRefKind, useStrictArgumentRefKinds: false); + } + [return: NotNullIfNotNull(nameof(method))] private static MethodSymbol? VisitMethodSymbolWithExtensionRewrite(BoundTreeRewriter rewriter, MethodSymbol? method) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index a87b77dbf5650..0a39857de9e50 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -81,7 +81,16 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo break; } - return MakeStaticAssignmentOperator(node.Syntax, loweredLeft, loweredRight, node.IsRef, used); + return MakeStaticAssignmentOperator(node.Syntax, loweredLeft, loweredRight, node.IsRef, used, AssignmentKind.SimpleAssignment); + } + + private enum AssignmentKind + { + SimpleAssignment, + CompoundAssignment, + IncrementDecrement, + Deconstruction, + NullCoalescingAssignment, } /// @@ -89,7 +98,7 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo /// Left and right sub-expressions must be in lowered form. /// private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, - bool used, bool isChecked, bool isCompoundAssignment) + bool used, bool isChecked, AssignmentKind assignmentKind) { switch (rewrittenLeft.Kind) { @@ -102,7 +111,7 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio indexerAccess.ArgumentNamesOpt, indexerAccess.ArgumentRefKindsOpt, rewrittenRight, - isCompoundAssignment, isChecked); + isCompoundAssignment: assignmentKind == AssignmentKind.CompoundAssignment, isChecked); case BoundKind.DynamicMemberAccess: var memberAccess = (BoundDynamicMemberAccess)rewrittenLeft; @@ -110,7 +119,7 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio memberAccess.Receiver, memberAccess.Name, rewrittenRight, - isCompoundAssignment, + isCompoundAssignment: assignmentKind == AssignmentKind.CompoundAssignment, isChecked).ToExpression(); case BoundKind.EventAccess: @@ -132,7 +141,7 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio throw ExceptionUtilities.Unreachable(); default: - return MakeStaticAssignmentOperator(syntax, rewrittenLeft, rewrittenRight, isRef: false, used: used); + return MakeStaticAssignmentOperator(syntax, rewrittenLeft, rewrittenRight, isRef: false, used: used, assignmentKind); } } @@ -168,7 +177,8 @@ private BoundExpression MakeStaticAssignmentOperator( BoundExpression rewrittenLeft, BoundExpression rewrittenRight, bool isRef, - bool used) + bool used, + AssignmentKind assignmentKind) { switch (rewrittenLeft.Kind) { @@ -192,7 +202,8 @@ private BoundExpression MakeStaticAssignmentOperator( false, default(ImmutableArray), rewrittenRight, - used); + used, + assignmentKind); } case BoundKind.IndexerAccess: @@ -212,7 +223,8 @@ private BoundExpression MakeStaticAssignmentOperator( indexerAccess.Expanded, indexerAccess.ArgsToParamsOpt, rewrittenRight, - used); + used, + assignmentKind); } case BoundKind.Local: @@ -248,7 +260,8 @@ private BoundExpression MakeStaticAssignmentOperator( sequence.Value, rewrittenRight, isRef, - used), + used, + assignmentKind), sequence.Type); } goto default; @@ -264,6 +277,17 @@ private BoundExpression MakeStaticAssignmentOperator( } } + private bool IsExtensionPropertyWithByValPossiblyStructReceiverWhichHasHomeAndCanChangeValueBetweenReads(BoundExpression rewrittenReceiver, PropertySymbol property) + { + return CanChangeValueBetweenReads(rewrittenReceiver, localsMayBeAssignedOrCaptured: true, structThisCanChangeValueBetweenReads: true) && + IsNewExtensionMemberWithByValPossiblyStructReceiver(property) && + CodeGen.CodeGenerator.HasHome(rewrittenReceiver, + CodeGen.CodeGenerator.AddressKind.ReadOnlyStrict, + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null); + } + private BoundExpression MakePropertyAssignment( SyntaxNode syntax, BoundExpression? rewrittenReceiver, @@ -273,7 +297,8 @@ private BoundExpression MakePropertyAssignment( bool expanded, ImmutableArray argsToParamsOpt, BoundExpression rewrittenRight, - bool used) + bool used, + AssignmentKind assignmentKind) { // Rewrite property assignment into call to setter. var setMethod = property.GetOwnOrInheritedSetMethod(); @@ -293,25 +318,73 @@ private BoundExpression MakePropertyAssignment( } ArrayBuilder? argTempsBuilder = null; + + bool needSpecialExtensionPropertyReceiverReadOrder = false; + ArrayBuilder? storesOpt = null; + + if (rewrittenReceiver is not null && + assignmentKind is not (AssignmentKind.CompoundAssignment or AssignmentKind.NullCoalescingAssignment or AssignmentKind.Deconstruction or AssignmentKind.IncrementDecrement) && + IsExtensionPropertyWithByValPossiblyStructReceiverWhichHasHomeAndCanChangeValueBetweenReads(rewrittenReceiver, property) && + (arguments.Length != 0 || !IsSafeForReordering(rewrittenRight, RefKind.None))) + { + // The receiver has location, but extension property/indexer takes receiver by value. + // This means that we need to ensure that the receiver value is read after + // any side-effecting arguments and right hand side are evaluated, so that the + // setter receives the last value of the receiver, not the value before the + // arguments/rhs were evaluated. Receiver side effects should be evaluated at + // the very beginning, of course. + + needSpecialExtensionPropertyReceiverReadOrder = true; + storesOpt = ArrayBuilder.GetInstance(); + } + +#if DEBUG + BoundExpression? rewrittenReceiverBeforePossibleCapture = rewrittenReceiver; +#endif arguments = VisitArgumentsAndCaptureReceiverIfNeeded( ref rewrittenReceiver, - forceReceiverCapturing: false, + forceReceiverCapturing: needSpecialExtensionPropertyReceiverReadOrder, arguments, property, argsToParamsOpt, argumentRefKindsOpt, - storesOpt: null, + storesOpt, ref argTempsBuilder); - arguments = MakeArguments( - arguments, - property, - expanded, - argsToParamsOpt, - ref argumentRefKindsOpt, - ref argTempsBuilder, - invokedAsExtensionMethod: false); + if (needSpecialExtensionPropertyReceiverReadOrder) + { +#if DEBUG + Debug.Assert(rewrittenReceiverBeforePossibleCapture != (object?)rewrittenReceiver); +#endif + Debug.Assert(storesOpt is { }); + Debug.Assert(storesOpt.Count != 0); + Debug.Assert(argTempsBuilder is not null); + + // Store everything that is non-trivial into a temporary; record the + // stores in storesToTemps and make the actual argument a reference to the temp. + arguments = ExtractSideEffectsFromArguments(arguments, property, expanded, argsToParamsOpt, ref argumentRefKindsOpt, storesOpt, argTempsBuilder); + + if (!IsSafeForReordering(rewrittenRight, RefKind.None)) + { + BoundLocal capturedRight = _factory.StoreToTemp(rewrittenRight, out BoundAssignmentOperator assignmentToTemp, refKind: RefKind.None); + argTempsBuilder.Add(capturedRight.LocalSymbol); + storesOpt.Add(assignmentToTemp); + rewrittenRight = capturedRight; + } + } + else + { + arguments = MakeArguments( + arguments, + property, + expanded, + argsToParamsOpt, + ref argumentRefKindsOpt, + ref argTempsBuilder, + invokedAsExtensionMethod: false); + } + var sideEffects = storesOpt is null ? [] : storesOpt.ToImmutableAndFree(); var argTemps = argTempsBuilder.ToImmutableAndFree(); if (used) @@ -342,7 +415,7 @@ private BoundExpression MakePropertyAssignment( return new BoundSequence( syntax, AppendToPossibleNull(argTemps, rhsTemp), - ImmutableArray.Create(setterCall), + sideEffects.Add(setterCall), // https://github.com/dotnet/roslyn/issues/78829 - there is no test coverage for 'sideEffects' on this code path boundRhs, rhsTemp.Type); } @@ -357,6 +430,7 @@ private BoundExpression MakePropertyAssignment( if (argTemps.IsDefaultOrEmpty) { + Debug.Assert(sideEffects.IsEmpty); return setterCall; } else @@ -364,7 +438,7 @@ private BoundExpression MakePropertyAssignment( return new BoundSequence( syntax, argTemps, - ImmutableArray.Empty, + sideEffects, setterCall, setMethod.ReturnType); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 54bf270ca36ab..c0d305fd7cc53 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -644,6 +644,18 @@ internal static bool IsCapturedPrimaryConstructorParameter(BoundExpression expre /// /// Visits all arguments of a method, doing any necessary rewriting for interpolated string handler conversions that /// might be present in the arguments and creating temps for any discard parameters. + /// + /// When is true (which means the receiver must be captured regardless of + /// interpolated string handler conversions needs), must be not null. + /// + /// If receiver is captured by this method: + /// - If is not null, the side effect of capturing is added to + /// and is changed to the captured value; + /// - Otherwise, is changed to a node with no locals, + /// the side effects of capturing are the side effects of the sequence and its result is the captured value. + /// + /// All temps introduced by this function for capturing purposes (including the temp capturing the receiver) are appended + /// to , which is allocated if 'null' on input. /// private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded( [NotNullIfNotNull(nameof(rewrittenReceiver))] ref BoundExpression? rewrittenReceiver, @@ -660,6 +672,7 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded var requiresInstanceReceiver = methodOrIndexer.RequiresInstanceReceiver() && methodOrIndexer is not MethodSymbol { MethodKind: MethodKind.Constructor } and not FunctionPointerMethodSymbol; Debug.Assert(!requiresInstanceReceiver || rewrittenReceiver != null || _inExpressionLambda); Debug.Assert(!forceReceiverCapturing || (requiresInstanceReceiver && rewrittenReceiver != null && storesOpt is object)); + Debug.Assert(!forceReceiverCapturing || methodOrIndexer is PropertySymbol); BoundLocal? receiverTemp = null; BoundAssignmentOperator? assignmentToTemp = null; @@ -673,41 +686,48 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded RefKind refKind; - if (forceReceiverCapturing) + if (methodOrIndexer.GetIsNewExtensionMember()) { - // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), - // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. - // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable - // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) - // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed - // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, - // SPEC VIOLATION: as value types. - - refKind = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter ? RefKind.Ref : RefKind.None; + refKind = GetNewExtensionMemberReceiverCaptureRefKind(rewrittenReceiver, methodOrIndexer); } else { - if (rewrittenReceiver.Type.IsReferenceType) + if (forceReceiverCapturing) { - refKind = RefKind.None; + // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), + // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. + // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable + // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) + // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed + // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, + // SPEC VIOLATION: as value types. + + refKind = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter ? RefKind.Ref : RefKind.None; } else { - refKind = rewrittenReceiver.GetRefKind(); - - if (refKind == RefKind.None && - CodeGenerator.HasHome(rewrittenReceiver, - CodeGenerator.AddressKind.Constrained, - _factory.CurrentFunction, - peVerifyCompatEnabled: false, - stackLocalsOpt: null)) + if (rewrittenReceiver.Type.IsReferenceType) + { + refKind = RefKind.None; + } + else { - refKind = RefKind.Ref; + refKind = rewrittenReceiver.GetRefKind(); + + if (refKind == RefKind.None && + CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.AddressKind.Constrained, + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null)) + { + refKind = RefKind.Ref; + } } } } - receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind); + receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind is RefKind.RefReadOnlyParameter ? RefKind.In : refKind); tempsOpt ??= ArrayBuilder.GetInstance(); tempsOpt.Add(receiverTemp.LocalSymbol); @@ -778,7 +798,9 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded BoundAssignmentOperator? extraRefInitialization = null; if (receiverTemp.LocalSymbol.IsRef && - CodeGenerator.IsPossibleReferenceTypeReceiverOfConstrainedCall(receiverTemp) && + (methodOrIndexer.GetIsNewExtensionMember() ? + !receiverTemp.Type.IsValueType : + CodeGenerator.IsPossibleReferenceTypeReceiverOfConstrainedCall(receiverTemp)) && !CodeGenerator.ReceiverIsKnownToReferToTempIfReferenceType(receiverTemp) && (forceReceiverCapturing || !CodeGenerator.IsSafeToDereferenceReceiverRefAfterEvaluatingArguments(rewrittenArguments))) @@ -923,6 +945,48 @@ static bool usesReceiver(BoundExpression argument) } } + private RefKind GetNewExtensionMemberReceiverCaptureRefKind(BoundExpression rewrittenReceiver, Symbol methodOrIndexer) + { + Debug.Assert(rewrittenReceiver.Type is { }); + Debug.Assert(methodOrIndexer.ContainingType.ExtensionParameter is { }); + + RefKind receiverRefKind = methodOrIndexer.ContainingType.ExtensionParameter.RefKind; + bool isReceiverTakenByValue = receiverRefKind == RefKind.None; + + if (rewrittenReceiver.Type.IsReferenceType || + (isReceiverTakenByValue && methodOrIndexer is MethodSymbol)) // Extension methods with by-value receivers capture by value as classic extension methods do. + { + return RefKind.None; + } + + if (isReceiverTakenByValue) + { + if (CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.AddressKind.ReadOnlyStrict, + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null)) + { + return RefKindExtensions.StrictIn; + } + + return RefKind.None; + } + + RefKind refKind = ExtensionMethodReferenceRewriter.ReceiverArgumentRefKindFromReceiverRefKind(receiverRefKind); + + if (CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.GetArgumentAddressKind(refKind), + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null)) + { + return refKind; + } + + return RefKind.None; + } + private void ReferToTempIfReferenceTypeReceiver(BoundLocal receiverTemp, ref BoundAssignmentOperator assignmentToTemp, out BoundAssignmentOperator? extraRefInitialization, ArrayBuilder temps) { Debug.Assert(assignmentToTemp.IsRef); @@ -944,7 +1008,7 @@ private void ReferToTempIfReferenceTypeReceiver(BoundLocal receiverTemp, ref Bou if (!receiverType.IsReferenceType) { // Store receiver ref to a different ref local - intermediate ref - var intermediateRef = _factory.Local(_factory.SynthesizedLocal(receiverType, refKind: RefKind.Ref)); + var intermediateRef = _factory.Local(_factory.SynthesizedLocal(receiverType, refKind: receiverTemp.LocalSymbol.RefKind)); temps.Add(intermediateRef.LocalSymbol); extraRefInitialization = assignmentToTemp.Update(intermediateRef, assignmentToTemp.Right, assignmentToTemp.IsRef, assignmentToTemp.Type); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 9c10562ecdfad..91f162a64b4ca 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -76,7 +76,7 @@ private BoundExpression VisitInstanceCompoundAssignmentOperator(BoundCompoundAss Debug.Assert(node.LeftConversion is null); - return MakeInstanceCompoundAssignmentOperatorResult(node.Syntax, node.Left, node.Right, node.Operator.Method, node.Operator.Kind.IsChecked()); + return MakeInstanceCompoundAssignmentOperatorResult(node.Syntax, node.Left, node.Right, node.Operator.Method, node.Operator.Kind.IsChecked(), AssignmentKind.CompoundAssignment); } private BoundExpression VisitBuiltInOrStaticCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used) @@ -231,19 +231,31 @@ BoundExpression rewriteAssignment(BoundExpression leftRead, BoundExpression righ { // We need to create a tree that ensures that receiver of 'set' is evaluated after the binary operation BoundLocal binaryResult = _factory.StoreToTemp(opFinal, out BoundAssignmentOperator assignmentToTemp, refKind: RefKind.None); - BoundExpression assignment = MakeAssignmentOperator(syntax, transformedLHS, binaryResult, used: used, isChecked: isChecked, isCompoundAssignment: true); + BoundExpression assignment = MakeAssignmentOperator(syntax, transformedLHS, binaryResult, used: used, isChecked: isChecked, AssignmentKind.CompoundAssignment); Debug.Assert(assignment.Type is { }); return new BoundSequence(syntax, [binaryResult.LocalSymbol], [assignmentToTemp], assignment, assignment.Type); } - return MakeAssignmentOperator(syntax, transformedLHS, opFinal, used: used, isChecked: isChecked, isCompoundAssignment: true); + return MakeAssignmentOperator(syntax, transformedLHS, opFinal, used: used, isChecked: isChecked, AssignmentKind.CompoundAssignment); } } private static bool IsNewExtensionMemberAccessWithByValPossiblyStructReceiver(BoundExpression transformedLHS) { - return transformedLHS is BoundPropertyAccess { PropertySymbol: { } property } && property.GetIsNewExtensionMember() && - property.ContainingType.ExtensionParameter is { RefKind: RefKind.None, Type.IsReferenceType: false }; + switch (transformedLHS) + { + case BoundPropertyAccess { PropertySymbol: { } property }: + return IsNewExtensionMemberWithByValPossiblyStructReceiver(property); + case BoundIndexerAccess { Indexer: { } indexer }: + return IsNewExtensionMemberWithByValPossiblyStructReceiver(indexer); + default: + return false; + } + } + + static bool IsNewExtensionMemberWithByValPossiblyStructReceiver(Symbol symbol) + { + return symbol.GetIsNewExtensionMember() && !symbol.IsStatic && symbol.ContainingType.ExtensionParameter is { RefKind: RefKind.None, Type.IsReferenceType: false }; } private BoundExpression? TransformPropertyOrEventReceiver(Symbol propertyOrEvent, BoundExpression? receiverOpt, ArrayBuilder stores, ArrayBuilder temps) @@ -277,31 +289,43 @@ private static bool IsNewExtensionMemberAccessWithByValPossiblyStructReceiver(Bo Debug.Assert(receiverOpt.Kind != BoundKind.TypeExpression); BoundExpression rewrittenReceiver = VisitExpression(receiverOpt); + Debug.Assert(rewrittenReceiver.Type is { }); BoundAssignmentOperator assignmentToTemp; + RefKind refKind; + bool isKnownToReferToTempIfReferenceType = false; - // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), - // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. - // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable - // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) - // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed - // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, - // SPEC VIOLATION: as value types. - Debug.Assert(rewrittenReceiver.Type is { }); - var variableRepresentsLocation = propertyOrEvent.GetIsNewExtensionMember() ? - !rewrittenReceiver.Type.IsReferenceType : - (rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter); + if (propertyOrEvent.GetIsNewExtensionMember()) + { + refKind = GetNewExtensionMemberReceiverCaptureRefKind(rewrittenReceiver, propertyOrEvent); + } + else + { + // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), + // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. + // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable + // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) + // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed + // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, + // SPEC VIOLATION: as value types. + + var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter; + refKind = variableRepresentsLocation ? RefKind.Ref : RefKind.None; + + isKnownToReferToTempIfReferenceType = !variableRepresentsLocation || rewrittenReceiver.Type.IsValueType || + !CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.AddressKind.Constrained, + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null); + } var receiverTemp = _factory.StoreToTemp( rewrittenReceiver, out assignmentToTemp, - refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None, - isKnownToReferToTempIfReferenceType: !variableRepresentsLocation || rewrittenReceiver.Type.IsValueType || - !CodeGenerator.HasHome(rewrittenReceiver, - CodeGenerator.AddressKind.Constrained, - _factory.CurrentFunction, - peVerifyCompatEnabled: false, - stackLocalsOpt: null)); + refKind: refKind is RefKind.RefReadOnlyParameter ? RefKind.In : refKind, + isKnownToReferToTempIfReferenceType: isKnownToReferToTempIfReferenceType); + temps.Add(receiverTemp.LocalSymbol); if (receiverTemp.LocalSymbol.IsRef && @@ -421,7 +445,38 @@ private BoundIndexerAccess TransformIndexerAccessContinued( PropertySymbol indexer = indexerAccess.Indexer; bool expanded = indexerAccess.Expanded; + rewrittenArguments = ExtractSideEffectsFromArguments(rewrittenArguments, indexer, expanded, argsToParamsOpt, ref argumentRefKinds, stores, temps); + + // This is a temporary object that will be rewritten away before the lowering completes. + return new BoundIndexerAccess( + syntax, + transformedReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + indexer, + rewrittenArguments, + argumentNamesOpt: default(ImmutableArray), + argumentRefKinds, + expanded: false, + accessorKind: indexerAccess.AccessorKind, + argsToParamsOpt: default(ImmutableArray), + defaultArguments: default(BitVector), + indexerAccess.Type); + } + /// + /// Store everything that is non-trivial into a temporary; record the + /// stores in and make the actual argument a reference to the temp. + /// Return resulting arguments. + /// + private ImmutableArray ExtractSideEffectsFromArguments( + ImmutableArray rewrittenArguments, + PropertySymbol indexer, + bool expanded, + ImmutableArray argsToParamsOpt, + ref ImmutableArray argumentRefKinds, + ArrayBuilder stores, + ArrayBuilder temps) + { ImmutableArray parameters = indexer.Parameters; BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter. ArrayBuilder storesToTemps = ArrayBuilder.GetInstance(rewrittenArguments.Length); @@ -451,16 +506,10 @@ private BoundIndexerAccess TransformIndexerAccessContinued( BoundAssignmentOperator storeToTemp; var boundTemp = _factory.StoreToTemp(array, out storeToTemp); - stores.Add(storeToTemp); - temps.Add(boundTemp.LocalSymbol); + storesToTemps.Add(storeToTemp); actualArguments[actualArguments.Length - 1] = boundTemp; } - // Step three: Now fill in the optional arguments. (Dev11 uses the getter for optional arguments in - // compound assignments, but for deconstructions we use the setter if the getter is missing.) - var accessor = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod(); - Debug.Assert(accessor is not null); - // For a call, step four would be to optimize away some of the temps. However, we need them all to prevent // duplicate side-effects, so we'll skip that step. @@ -482,20 +531,7 @@ private BoundIndexerAccess TransformIndexerAccessContinued( argumentRefKinds = GetRefKindsOrNull(refKinds); refKinds.Free(); - // This is a temporary object that will be rewritten away before the lowering completes. - return new BoundIndexerAccess( - syntax, - transformedReceiver, - initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, - indexer, - rewrittenArguments, - argumentNamesOpt: default(ImmutableArray), - argumentRefKinds, - expanded: false, - accessorKind: indexerAccess.AccessorKind, - argsToParamsOpt: default(ImmutableArray), - defaultArguments: default(BitVector), - indexerAccess.Type); + return rewrittenArguments; } private BoundExpression TransformImplicitIndexerAccess( diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs index 8b1a73e3edd84..14caaad3e0e44 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs @@ -252,7 +252,7 @@ static bool canReorderTargetAssignments(ArrayBuilder? temps = null; + bool needSpecialExtensionPropertyReceiverReadOrder = false; + ArrayBuilder? storesOpt = null; + + if (IsExtensionPropertyWithByValPossiblyStructReceiverWhichHasHomeAndCanChangeValueBetweenReads(rewrittenReceiver, indexer)) + { + // The receiver has location, but extension indexer takes receiver by value. + // This means that we need to ensure that the receiver value is read after + // any side-effecting arguments are evaluated, so that the + // setter receives the last value of the receiver, not the value before the + // arguments were evaluated. Receiver side effects should be evaluated at + // the very beginning, of course. + + needSpecialExtensionPropertyReceiverReadOrder = true; + storesOpt = ArrayBuilder.GetInstance(); + } + +#if DEBUG + BoundExpression? rewrittenReceiverBeforePossibleCapture = rewrittenReceiver; +#endif ImmutableArray rewrittenArguments = VisitArgumentsAndCaptureReceiverIfNeeded( ref rewrittenReceiver, - forceReceiverCapturing: false, + forceReceiverCapturing: needSpecialExtensionPropertyReceiverReadOrder, arguments, indexer, argsToParamsOpt, argumentRefKindsOpt, - storesOpt: null, + storesOpt, ref temps); - rewrittenArguments = MakeArguments( - rewrittenArguments, - indexer, - expanded, - argsToParamsOpt, - ref argumentRefKindsOpt, - ref temps); + if (needSpecialExtensionPropertyReceiverReadOrder) + { +#if DEBUG + Debug.Assert(rewrittenReceiverBeforePossibleCapture != (object?)rewrittenReceiver); +#endif + Debug.Assert(storesOpt is { }); + Debug.Assert(storesOpt.Count != 0); + Debug.Assert(temps is not null); + + // Store everything that is non-trivial into a temporary; record the + // stores in storesToTemps and make the actual argument a reference to the temp. + rewrittenArguments = ExtractSideEffectsFromArguments(rewrittenArguments, indexer, expanded, argsToParamsOpt, ref argumentRefKindsOpt, storesOpt, temps); + } + else + { + rewrittenArguments = MakeArguments( + rewrittenArguments, + indexer, + expanded, + argsToParamsOpt, + ref argumentRefKindsOpt, + ref temps); + } + var sideEffects = storesOpt is null ? [] : storesOpt.ToImmutableAndFree(); BoundExpression call = MakePropertyGetAccess(syntax, rewrittenReceiver, indexer, rewrittenArguments, argumentRefKindsOpt, getMethod); Debug.Assert(call.Type is not null); if (temps.Count == 0) { + Debug.Assert(sideEffects.IsEmpty); temps.Free(); return call; } @@ -189,7 +228,7 @@ private BoundExpression MakeIndexerAccess( return new BoundSequence( syntax, temps.ToImmutableAndFree(), - ImmutableArray.Empty, + sideEffects, call, call.Type); } @@ -573,6 +612,7 @@ private BoundExpression GetUnderlyingIndexerOrSliceAccess( // callers do the caching instead // Tracked by https://github.com/dotnet/roslyn/issues/71056 AddPlaceholderReplacement(argumentPlaceholder, integerArgument); + // https://github.com/dotnet/roslyn/issues/78829 - Do we need to do something special for recievers of extension indexers here? ImmutableArray rewrittenArguments = VisitArgumentsAndCaptureReceiverIfNeeded( ref receiver, forceReceiverCapturing: false, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs index 600fd56b4b89e..cfe3faf35e248 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs @@ -45,13 +45,13 @@ BoundExpression rewriteNullCoalscingAssignmentStandard() { // We need to create a tree that ensures that receiver of 'set' is evaluated after the right hand side value BoundLocal rightResult = _factory.StoreToTemp(loweredRight, out BoundAssignmentOperator assignmentToTemp, refKind: RefKind.None); - assignment = MakeAssignmentOperator(syntax, transformedLHS, rightResult, used: true, isChecked: false, isCompoundAssignment: false); + assignment = MakeAssignmentOperator(syntax, transformedLHS, rightResult, used: true, isChecked: false, AssignmentKind.NullCoalescingAssignment); Debug.Assert(assignment.Type is { }); assignment = new BoundSequence(syntax, [rightResult.LocalSymbol], [assignmentToTemp], assignment, assignment.Type); } else { - assignment = MakeAssignmentOperator(syntax, transformedLHS, loweredRight, used: true, isChecked: false, isCompoundAssignment: false); + assignment = MakeAssignmentOperator(syntax, transformedLHS, loweredRight, used: true, isChecked: false, AssignmentKind.NullCoalescingAssignment); } // lhsRead ?? (transformedLHS = loweredRight) @@ -120,7 +120,7 @@ BoundExpression rewriteNullCoalescingAssignmentForValueType() temps.Add(tmp.LocalSymbol); // tmp = loweredRight; - var tmpAssignment = MakeAssignmentOperator(node.Syntax, tmp, loweredRight, used: true, isChecked: false, isCompoundAssignment: false); + var tmpAssignment = MakeAssignmentOperator(node.Syntax, tmp, loweredRight, used: true, isChecked: false, AssignmentKind.SimpleAssignment); Debug.Assert(transformedLHS.Type.GetNullableUnderlyingType().Equals(tmp.Type.StrippedType(), TypeCompareKind.AllIgnoreOptions)); @@ -133,7 +133,7 @@ BoundExpression rewriteNullCoalescingAssignmentForValueType() MakeConversionNode(tmp, transformedLHS.Type, @checked: false, markAsChecked: true), used: true, isChecked: false, - isCompoundAssignment: false); + AssignmentKind.NullCoalescingAssignment); // lhsRead.HasValue var lhsReadHasValue = BoundCall.Synthesized(leftOperand.Syntax, lhsRead, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, hasValue); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs index 0ec53590e2ecc..b6189ff435b1d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs @@ -398,7 +398,7 @@ private void AddObjectInitializer( // Rewrite simple assignment to field/property. var rewrittenRight = VisitExpression(right); Debug.Assert(assignment.Type.IsDynamic() || TypeSymbol.Equals(rewrittenAccess.Type, assignment.Type, TypeCompareKind.AllIgnoreOptions)); - result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, isRef: assignment.IsRef, used: false)); + result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, isRef: assignment.IsRef, used: false, AssignmentKind.SimpleAssignment)); return; } } @@ -470,7 +470,7 @@ private void AddObjectInitializer( // Rewrite simple assignment to field/property. var rewrittenRight = VisitExpression(right); Debug.Assert(TypeSymbol.Equals(rewrittenAccess.Type, assignment.Type, TypeCompareKind.AllIgnoreOptions)); - result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, used: false)); + result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, used: false, AssignmentKind.SimpleAssignment)); return; } @@ -503,7 +503,7 @@ private void AddObjectInitializer( // Rewrite as simple assignment. var rewrittenRight = VisitExpression(right); Debug.Assert(TypeSymbol.Equals(rewrittenAccess.Type, assignment.Type, TypeCompareKind.AllIgnoreOptions)); - result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, used: false)); + result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, used: false, AssignmentKind.SimpleAssignment)); return; } @@ -537,7 +537,7 @@ private void AddObjectInitializer( { var rewrittenRight = VisitExpression(right); Debug.Assert(TypeSymbol.Equals(rewrittenAccess.Type, assignment.Type, TypeCompareKind.AllIgnoreOptions)); - result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, isRef: false, used: false)); + result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, isRef: false, used: false, AssignmentKind.SimpleAssignment)); return; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs index fd76f5c2b6202..7b60f3b3289c2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs @@ -450,10 +450,10 @@ private BoundExpression VisitInstanceIncrementOperator(BoundIncrementOperator no Debug.Assert(node.OperandConversion is null); - return MakeInstanceCompoundAssignmentOperatorResult(node.Syntax, node.Operand, rightOpt: null, node.MethodOpt, node.OperatorKind.IsChecked()); + return MakeInstanceCompoundAssignmentOperatorResult(node.Syntax, node.Operand, rightOpt: null, node.MethodOpt, node.OperatorKind.IsChecked(), AssignmentKind.IncrementDecrement); } - private BoundExpression MakeInstanceCompoundAssignmentOperatorResult(SyntaxNode syntax, BoundExpression left, BoundExpression? rightOpt, MethodSymbol operatorMethod, bool isChecked) + private BoundExpression MakeInstanceCompoundAssignmentOperatorResult(SyntaxNode syntax, BoundExpression left, BoundExpression? rightOpt, MethodSymbol operatorMethod, bool isChecked, AssignmentKind assignmentKind) { TypeSymbol? operandType = left.Type; //type of the variable being incremented Debug.Assert(operandType is { }); @@ -479,7 +479,7 @@ private BoundExpression MakeInstanceCompoundAssignmentOperatorResult(SyntaxNode if (operandType.IsValueType) { BoundCall increment = makeIncrementCall(syntax, boundTemp, rightOpt, operatorMethod); - BoundExpression assignBack = makeAssignmentBack(syntax, transformedLHS, boundTemp, isChecked); + BoundExpression assignBack = makeAssignmentBack(syntax, transformedLHS, boundTemp, isChecked, assignmentKind); tempInitializers.Add(increment); tempInitializers.Add(assignBack); @@ -495,7 +495,7 @@ private BoundExpression MakeInstanceCompoundAssignmentOperatorResult(SyntaxNode } BoundCall increment = makeIncrementCall(syntax, boundTemp, rightOpt, operatorMethod); - BoundExpression assignBack = makeAssignmentBack(syntax, transformedLHS, boundTemp, isChecked); + BoundExpression assignBack = makeAssignmentBack(syntax, transformedLHS, boundTemp, isChecked, assignmentKind); // (object)default(T) != null var isNotClass = _factory.IsNotNullReference(_factory.Default(operandType)); @@ -525,9 +525,9 @@ static BoundCall makeIncrementCall(SyntaxNode syntax, BoundLocal boundTemp, Boun return BoundCall.Synthesized(syntax, boundTemp, initialBindingReceiverIsSubjectToCloning: ThreeState.False, operatorMethod, rightOpt is null ? [] : [rightOpt]); } - BoundExpression makeAssignmentBack(SyntaxNode syntax, BoundExpression transformedLHS, BoundLocal boundTemp, bool isChecked) + BoundExpression makeAssignmentBack(SyntaxNode syntax, BoundExpression transformedLHS, BoundLocal boundTemp, bool isChecked, AssignmentKind assignmentKind) { - return MakeAssignmentOperator(syntax, transformedLHS, boundTemp, used: false, isChecked: isChecked, isCompoundAssignment: false); + return MakeAssignmentOperator(syntax, transformedLHS, boundTemp, used: false, isChecked: isChecked, assignmentKind); } } @@ -661,7 +661,7 @@ BoundExpression rewriteWithNotRefOperand( // prefix: temp = (X)(T.Increment((T)operand))); operand = temp; // postfix: temp = operand; operand = (X)(T.Increment((T)temp))); - tempInitializers.Add(MakeAssignmentOperator(syntax, boundTemp, isPrefix ? newValue : MakeRValue(transformedLHS), used: false, isChecked: isChecked, isCompoundAssignment: false)); + tempInitializers.Add(MakeAssignmentOperator(syntax, boundTemp, isPrefix ? newValue : MakeRValue(transformedLHS), used: false, isChecked: isChecked, AssignmentKind.SimpleAssignment)); if (!isPrefix && IsNewExtensionMemberAccessWithByValPossiblyStructReceiver(transformedLHS)) { @@ -669,11 +669,11 @@ BoundExpression rewriteWithNotRefOperand( BoundLocal incrementResult = _factory.StoreToTemp(newValue, out BoundAssignmentOperator assignmentToTemp, refKind: RefKind.None); tempSymbols.Add(incrementResult.LocalSymbol); tempInitializers.Add(assignmentToTemp); - tempInitializers.Add(MakeAssignmentOperator(syntax, transformedLHS, incrementResult, used: false, isChecked: isChecked, isCompoundAssignment: false)); + tempInitializers.Add(MakeAssignmentOperator(syntax, transformedLHS, incrementResult, used: false, isChecked: isChecked, AssignmentKind.IncrementDecrement)); } else { - tempInitializers.Add(MakeAssignmentOperator(syntax, transformedLHS, isPrefix ? boundTemp : newValue, used: false, isChecked: isChecked, isCompoundAssignment: false)); + tempInitializers.Add(MakeAssignmentOperator(syntax, transformedLHS, isPrefix ? boundTemp : newValue, used: false, isChecked: isChecked, AssignmentKind.IncrementDecrement)); } // prefix: Seq( operand initializers; temp = (T)(operand + 1); operand = temp; result: temp) @@ -700,7 +700,7 @@ BoundExpression rewriteWithRefOperand( var tempValue = isPrefix ? newValue : MakeRValue(operand); Debug.Assert(tempValue.Type is { }); - var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, used: false, isChecked: isChecked, isCompoundAssignment: false); + var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, used: false, isChecked: isChecked, AssignmentKind.SimpleAssignment); var operandValue = isPrefix ? boundTemp : newValue; var tempAssignedAndOperandValue = new BoundSequence( @@ -712,7 +712,7 @@ BoundExpression rewriteWithRefOperand( // prefix: operand = Seq{temp = (T)(operand + 1); temp;} // postfix: operand = Seq{temp = operand; ; (T)(temp + 1);} - BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, used: false, isChecked: isChecked, isCompoundAssignment: false); + BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, used: false, isChecked: isChecked, AssignmentKind.IncrementDecrement); // prefix: Seq{operand initializers; operand = Seq{temp = (T)(operand + 1); temp;} result: temp} // postfix: Seq{operand initializers; operand = Seq{temp = operand; ; (T)(temp + 1);} result: temp} diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs index 66fae04bf79cc..9607380fe911c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs @@ -535,10 +535,10 @@ internal static bool IsComplexConditionalInitializationOfReceiverRef( if (assignment is { IsRef: true, - Left: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.Ref } receiverRefLocal }, + Left: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: not RefKind.None } receiverRefLocal }, Right: BoundComplexConditionalReceiver { - ValueTypeReceiver: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.Ref } } valueTypeReceiver, + ValueTypeReceiver: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: not RefKind.None } } valueTypeReceiver, ReferenceTypeReceiver: BoundSequence { Locals.IsEmpty: true, @@ -548,7 +548,7 @@ internal static bool IsComplexConditionalInitializationOfReceiverRef( { IsRef: false, Left: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.None } referenceTypeClone }, - Right: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.Ref } originalReceiverReference } + Right: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: not RefKind.None } originalReceiverReference } } ], Value: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.None } } referenceTypeReceiver @@ -563,6 +563,7 @@ internal static bool IsComplexConditionalInitializationOfReceiverRef( && !receiverRefLocal.Type.IsReferenceType && !receiverRefLocal.Type.IsValueType && valueTypeReceiver.Type.Equals(receiverRefLocal.Type, TypeCompareKind.AllIgnoreOptions) + && receiverRefLocal.RefKind == valueTypeReceiver.LocalSymbol.RefKind && referenceTypeReceiver.Type.Equals(receiverRefLocal.Type, TypeCompareKind.AllIgnoreOptions) ) { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs index 6b01995d2246c..91e546fcc8f06 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs @@ -1261,5 +1261,114 @@ static IEnumerable Test() comp1.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor); CompileAndVerify(comp1).VerifyDiagnostics(); } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79436")] + public void ParamArrayOrderOfEvaluation() + { + var text1 = @" +public struct S1 +{ + public int this[int i, params int[] j] + { + get + { + System.Console.Write(""get_Item ""); + System.Console.Write(i); + System.Console.Write(j[0]); + System.Console.Write("" ""); + return 0; + } + set + { + System.Console.Write(""set_Item ""); + System.Console.Write(i); + System.Console.Write(j[0]); + System.Console.Write(value); + } + } +} + +class Program +{ + static void Main() + { + Test1(default); + System.Console.WriteLine(); + Test2(default); + } + + static void Test1(S1 s) + { + s[Get1(), Get2()] += Get3(); + } + + static void Test2(S1 s) + { + s[Get1(), [Get2()]] += Get3(); + } + + public static int Get1() + { + System.Console.Write(""Get1 ""); + return 1; + } + + public static int Get2() + { + System.Console.Write(""Get2 ""); + return 2; + } + + public static int Get3() + { + System.Console.Write(""Get3 ""); + return 3; + } +}"; + var comp1 = CreateCompilation(text1, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp1, expectedOutput: @" +Get1 Get2 get_Item 12 Get3 set_Item 123 +Get1 Get2 get_Item 12 Get3 set_Item 123 +").VerifyDiagnostics(); + + var expectedIL = @" +{ + // Code size 49 (0x31) + .maxstack 6 + .locals init (S1& V_0, + int V_1, + int[] V_2) + IL_0000: nop + IL_0001: ldarga.s V_0 + IL_0003: stloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: stloc.1 + IL_000a: ldc.i4.1 + IL_000b: newarr ""int"" + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: call ""int Program.Get2()"" + IL_0017: stelem.i4 + IL_0018: stloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: ldloc.0 + IL_001d: ldloc.1 + IL_001e: ldloc.2 + IL_001f: call ""int S1.this[int, params int[]].get"" + IL_0024: call ""int Program.Get3()"" + IL_0029: add + IL_002a: call ""void S1.this[int, params int[]].set"" + IL_002f: nop + IL_0030: ret +} +"; + + verifier.VerifyIL("Program.Test1", expectedIL); + + verifier.VerifyIL("Program.Test2", expectedIL); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index 2628ca1f35626..ba9e2333b2299 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -21049,6 +21049,7 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByRef(bool useMetadataRef) { @@ -21080,20 +21081,70 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument """; var exeSource = """ - int i = 1; - i.M($""); - System.Console.Write(i); - i = 4; - E.M(ref i, $""); - System.Console.Write(i); + class Program + { + static void Main() + { + int i = 1; + Test1(ref i); + System.Console.Write(i); + i = 4; + Test2(ref i); + System.Console.Write(i); + } + + static void Test1(ref int i) + { + i.M($""); + } + + static void Test2(ref int i) + { + E.M(ref i, $""); + } + } """; - var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "123423" : null; - CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + var expectedOutput = "123423"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput).VerifyDiagnostics(); - var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net90); + verifier.VerifyIL("Program.Test1", """ + { + // Code size 17 (0x11) + .maxstack 4 + .locals init (int& V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj "InterpolationHandler..ctor(int, int, ref int)" + IL_000b: call "void E.M(ref int, InterpolationHandler)" + IL_0010: ret + } + """); - CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) + verifier.VerifyIL("Program.Test2", """ + { + // Code size 17 (0x11) + .maxstack 4 + .locals init (int& V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj "InterpolationHandler..ctor(int, int, ref int)" + IL_000b: call "void E.M(ref int, InterpolationHandler)" + IL_0010: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) .VerifyDiagnostics(); } @@ -21143,15 +21194,116 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] - public void InterpolationHandler_ReceiverParameter_ByIn_WithConstantReceiver(bool useMetadataRef) + public void InterpolationHandler_ReceiverParameter_Generic_ByRef(bool useMetadataRef) { var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, ref TR i) + { + System.Console.Write(i); + i = (TR)(object)2; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public static class E + { + extension(ref T i) where T : struct + { + public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h) + { + System.Console.Write(i); + i = (T)(object)3; + } + } + } + """; + + var exeSource = """ + class Program + { + static void Main() + { + int i = 1; + Test1(ref i); + System.Console.Write(i); + i = 4; + Test2(ref i); + System.Console.Write(i); + } + + static void Test1(ref T i) where T : struct + { + i.M($""); + } + + static void Test2(ref T i) where T : struct + { + E.M(ref i, $""); + } + } + """; + + var expectedOutput = "123423"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", """ + { + // Code size 17 (0x11) + .maxstack 4 + .locals init (T& V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj "InterpolationHandler..ctor(int, int, ref T)" + IL_000b: call "void E.M(ref T, InterpolationHandler)" + IL_0010: ret + } + """); + + verifier.VerifyIL("Program.Test2", """ + { + // Code size 17 (0x11) + .maxstack 4 + .locals init (T& V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj "InterpolationHandler..ctor(int, int, ref T)" + IL_000b: call "void E.M(ref T, InterpolationHandler)" + IL_0010: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] + [CombinatorialData] + public void InterpolationHandler_ReceiverParameter_ByIn_WithConstantReceiver(bool useMetadataRef, [CombinatorialValues("ref readonly", "in")] string refkind) + { + var src = $$$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, in int i) + public InterpolationHandler(int literalLength, int formattedCount, {{{refkind}}} int i) { System.Console.Write(i); System.Runtime.CompilerServices.Unsafe.AsRef(in i)++; @@ -21162,7 +21314,7 @@ public void AppendLiteral(string value) { } public static class E { - extension(in int i) + extension({{{refkind}}} int i) { public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h) { @@ -21173,12 +21325,64 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument """; var exeSource = """ - 1.M($""); - E.M(3, $""); + #pragma warning disable CS9193 // Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + + class Program + { + static void Main() + { + Test1(); + Test2(); + } + + static void Test1() + { + 1.M($""); + } + + static void Test2() + { + E.M(3, $""); + } + } """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "1234" : null; - CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", $$$""" + { + // Code size 19 (0x13) + .maxstack 4 + .locals init (int V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.0 + IL_0006: ldloca.s V_0 + IL_0008: newobj "InterpolationHandler..ctor(int, int, {{{refkind}}} int)" + IL_000d: call "void E.M({{{refkind}}} int, InterpolationHandler)" + IL_0012: ret + } + """); + + verifier.VerifyIL("Program.Test2", $$$""" + { + // Code size 19 (0x13) + .maxstack 4 + .locals init (int V_0) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.0 + IL_0006: ldloca.s V_0 + IL_0008: newobj "InterpolationHandler..ctor(int, int, {{{refkind}}} int)" + IL_000d: call "void E.M({{{refkind}}} int, InterpolationHandler)" + IL_0012: ret + } + """); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net90); @@ -21485,6 +21689,7 @@ .locals init (MyStruct V_0) } [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_StructReceiverParameter_ByValueThroughField(bool useMetadataRef) { @@ -21529,44 +21734,476 @@ public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerA int Increment() => E.field.i++; """; - // Should be 0033, https://github.com/dotnet/roslyn/issues/79379 - var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "1033" : null; - var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) + var expectedOutput = "0033"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); verifier.VerifyIL("", """ { - // Code size 61 (0x3d) + // Code size 51 (0x33) .maxstack 5 - .locals init (MyStruct& V_0, - MyStruct V_1) - IL_0000: ldsflda "MyStruct E.field" + .locals init (MyStruct V_0) + IL_0000: ldsfld "MyStruct E.field" IL_0005: stloc.0 IL_0006: ldloc.0 - IL_0007: ldobj "MyStruct" - IL_000c: call "int Program.<
$>g__Increment|0_0()" - IL_0011: ldc.i4.0 - IL_0012: ldc.i4.0 - IL_0013: ldloc.0 - IL_0014: ldobj "MyStruct" - IL_0019: newobj "InterpolationHandler..ctor(int, int, MyStruct)" - IL_001e: call "void E.M(MyStruct, int, InterpolationHandler)" - IL_0023: ldsfld "MyStruct E.field" - IL_0028: stloc.1 - IL_0029: ldloc.1 - IL_002a: call "int Program.<
$>g__Increment|0_0()" - IL_002f: ldc.i4.0 - IL_0030: ldc.i4.0 - IL_0031: ldloc.1 - IL_0032: newobj "InterpolationHandler..ctor(int, int, MyStruct)" - IL_0037: call "void E.M(MyStruct, int, InterpolationHandler)" - IL_003c: ret + IL_0007: call "int Program.<
$>g__Increment|0_0()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, MyStruct)" + IL_0014: call "void E.M(MyStruct, int, InterpolationHandler)" + IL_0019: ldsfld "MyStruct E.field" + IL_001e: stloc.0 + IL_001f: ldloc.0 + IL_0020: call "int Program.<
$>g__Increment|0_0()" + IL_0025: ldc.i4.0 + IL_0026: ldc.i4.0 + IL_0027: ldloc.0 + IL_0028: newobj "InterpolationHandler..ctor(int, int, MyStruct)" + IL_002d: call "void E.M(MyStruct, int, InterpolationHandler)" + IL_0032: ret } """); - var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net90); + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); - CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] + [CombinatorialData] + public void InterpolationHandler_StructReceiverParameter_Generic_ByValueThroughField(bool useMetadataRef) + { + var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, TR s) + { + System.Console.Write(((MyStruct)(object)s).i); + E.field.i++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public struct MyStruct + { + public int i; + } + + public static class E + { + extension(T s) + { + public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h) + { + System.Console.Write(((MyStruct)(object)s).i); + E.field.i++; + } + } + } + + public static class E + { + public static T field; + } + """; + + var exeSource = """ + class Porgram + { + static void Main() + { + Test1(); + Test2(); + Test3(); + Test4(); + } + + static void Test1() + { + E.field.M(Increment(), $""); + } + + static void Test2() + { + E.M(E.field, Increment(), $""); + } + + static void Test3() where T : struct + { + E.field.M(Increment(), $""); + } + + static void Test4() where T : struct + { + E.M(E.field, Increment(), $""); + } + + static int Increment() => E.field.i++; + } + """; + + var expectedOutput = "00336699"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + + var expectedIL = """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """; + + verifier.VerifyIL("Porgram.Test1", expectedIL); + + verifier.VerifyIL("Porgram.Test2", expectedIL); + + verifier.VerifyIL("Porgram.Test3", expectedIL); + + verifier.VerifyIL("Porgram.Test4", expectedIL); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] + [CombinatorialData] + public void InterpolationHandler_StructReceiverParameter_GenericStruct_ByValueThroughField(bool useMetadataRef) + { + var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, TR s) + { + System.Console.Write(((MyStruct)(object)s).i); + E.field.i++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public struct MyStruct + { + public int i; + } + + public static class E + { + extension(T s) where T : struct + { + public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h) + { + System.Console.Write(((MyStruct)(object)s).i); + E.field.i++; + } + } + } + + public static class E + { + public static T field; + } + """; + + var exeSource = """ + class Porgram + { + static void Main() + { + Test3(); + Test4(); + } + + static void Test3() where T : struct + { + E.field.M(Increment(), $""); + } + + static void Test4() where T : struct + { + E.M(E.field, Increment(), $""); + } + + static int Increment() => E.field.i++; + } + """; + + var expectedOutput = "0033"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + + var expectedIL = """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """; + + verifier.VerifyIL("Porgram.Test3", expectedIL); + + verifier.VerifyIL("Porgram.Test4", expectedIL); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] + [CombinatorialData] + public void InterpolationHandler_ClassReceiverParameter_GenericClass_ByValueThroughField(bool useMetadataRef) + { + var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, TR s) + { + System.Console.Write(((MyClass)(object)s).i); + E.field = new MyClass() { i = E.field.i + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public class MyClass + { + public int i; + } + + public static class E + { + extension(T s) + { + public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h) + { + System.Console.Write(((MyClass)(object)s).i); + E.field = new MyClass() { i = E.field.i + 1 }; + } + } + } + + public static class E + { + public static T field; + } + """; + + var exeSource = """ + class Porgram + { + static void Main() + { + E.field = new MyClass(); + Test1(); + Test2(); + Test3(); + Test4(); + } + + static void Test1() + { + E.field.M(Increment(), $""); + } + + static void Test2() + { + E.M(E.field, Increment(), $""); + } + + static void Test3() where T : class + { + E.field.M(Increment(), $""); + } + + static void Test4() where T : class + { + E.M(E.field, Increment(), $""); + } + + static int Increment() => (E.field = new MyClass() { i = E.field.i + 1 }).i; + } + """; + + var expectedOutput = "00336699"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + + var expectedIL = """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """; + + verifier.VerifyIL("Porgram.Test1", expectedIL); + + verifier.VerifyIL("Porgram.Test2", expectedIL); + + verifier.VerifyIL("Porgram.Test3", expectedIL); + + verifier.VerifyIL("Porgram.Test4", expectedIL); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] + [CombinatorialData] + public void InterpolationHandler_ClassReceiverParameter_Generic_ByValueThroughField(bool useMetadataRef) + { + var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, TR s) + { + System.Console.Write(((MyClass)(object)s).i); + E.field = new MyClass() { i = E.field.i + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public class MyClass + { + public int i; + } + + public static class E + { + extension(T s) where T : class + { + public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h) + { + System.Console.Write(((MyClass)(object)s).i); + E.field = new MyClass() { i = E.field.i + 1 }; + } + } + } + + public static class E + { + public static T field; + } + """; + + var exeSource = """ + class Porgram + { + static void Main() + { + E.field = new MyClass(); + Test3(); + Test4(); + } + + static void Test3() where T : class + { + E.field.M(Increment(), $""); + } + + static void Test4() where T : class + { + E.M(E.field, Increment(), $""); + } + + static int Increment() => (E.field = new MyClass() { i = E.field.i + 1 }).i; + } + """; + + var expectedOutput = "0033"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + + verifier.VerifyIL("Porgram.Test3", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + verifier.VerifyIL("Porgram.Test4", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) .VerifyDiagnostics(); } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 38ae48a5f4363..017ce7ae4b785 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -5567,7 +5567,7 @@ static class E } [Fact] - public void PropertyAccess_CompoundAssignment_01() + public void PropertyAccess_Set_01() { var src = """ static class E @@ -5578,8 +5578,6 @@ public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; return 0; } set @@ -5590,13 +5588,35 @@ public int P1 } } -struct S1 +public struct S1 { public int F1; public void Test() { - this.P1 += Program.Get1(); + this.P1 = Program.Get1(); + } + + public int P2 + { + get + { + return 0; + } + set + { + System.Console.Write(F1); + } + } + + public void Test2() + { + this.P2 = Program.Get1(); + } + + public void Test3() + { + E.set_P1(this, Program.Get1()); } } @@ -5615,11 +5635,23 @@ static void Main() F = new S1 { F1 = 123 }; F.Test(); System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test2(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test3(); + System.Console.Write(F.F1); } static void Test() { - F.P1 += Get1(); + F.P1 = Get1(); } public static int Get1() @@ -5631,58 +5663,77 @@ public static int Get1() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124:124124:123124", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 37 (0x25) - .maxstack 3 + // Code size 25 (0x19) + .maxstack 2 .locals init (int V_0) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: ldobj ""S1"" - IL_000c: call ""int E.get_P1(S1)"" - IL_0011: call ""int Program.Get1()"" - IL_0016: add - IL_0017: stloc.0 - IL_0018: ldobj ""S1"" - IL_001d: ldloc.0 - IL_001e: call ""void E.set_P1(S1, int)"" - IL_0023: nop - IL_0024: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldobj ""S1"" + IL_0011: ldloc.0 + IL_0012: call ""void E.set_P1(S1, int)"" + IL_0017: nop + IL_0018: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 33 (0x21) + // Code size 21 (0x15) .maxstack 2 .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""int E.get_P1(S1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: stloc.0 - IL_0013: ldarg.0 - IL_0014: ldobj ""S1"" - IL_0019: ldloc.0 - IL_001a: call ""void E.set_P1(S1, int)"" - IL_001f: nop - IL_0020: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(S1, int)"" + IL_0013: nop + IL_0014: ret } "); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int P1 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1).P1 = 1; + } +} +"""; + + var comp2 = CreateCompilation([src2]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void PropertyAccess_CompoundAssignment_02(string refKind) + public void PropertyAccess_Set_02(string refKind) { var src = $$$""" static class E @@ -5711,7 +5762,7 @@ struct S1 public void Test() { - this.P1 += Program.Get1(); + this.P1 = Program.Get1(); } } @@ -5734,7 +5785,7 @@ static void Main() static void Test() { - F.P1 += Get1(); + F.P1 = Get1(); } public static int Get1() @@ -5745,40 +5796,34 @@ public static int Get1() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 25 (0x19) - .maxstack 3 + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: call ""int E.get_P1(" + refKind + @" S1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_0017: nop - IL_0018: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0010: nop + IL_0011: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 21 (0x15) - .maxstack 3 + // Code size 14 (0xe) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldarg.0 - IL_0003: call ""int E.get_P1(" + refKind + @" S1)"" - IL_0008: call ""int Program.Get1()"" - IL_000d: add - IL_000e: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_0013: nop - IL_0014: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_000c: nop + IL_000d: ret } "); @@ -5797,35 +5842,35 @@ class Program { static void Test() { - default(S1).P1 += 1; + default(S1).P1 = 1; } } """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2]); switch (refKind) { case "ref": comp2.VerifyDiagnostics( // (15,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P1 += 1; + // default(S1).P1 = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) ); break; case "ref readonly": comp2.VerifyDiagnostics( // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P1 += 1; + // default(S1).P1 = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 += 1; + // default(S1).P1 = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) ); break; case "in": comp2.VerifyDiagnostics( // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 += 1; + // default(S1).P1 = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) ); break; @@ -5835,7 +5880,7 @@ static void Test() } [Fact] - public void PropertyAccess_CompoundAssignment_03() + public void PropertyAccess_Set_03() { var src = """ static class E @@ -5876,7 +5921,7 @@ static void Main() static void Test() { - F.P1 += Get1(); + F.P1 = Get1(); } static int Get1() @@ -5887,29 +5932,27 @@ static int Get1() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 25 (0x19) - .maxstack 3 + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: ldsfld ""C1 Program.F"" - IL_0006: dup - IL_0007: call ""int E.get_P1(C1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: call ""void E.set_P1(C1, int)"" - IL_0017: nop - IL_0018: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(C1, int)"" + IL_0010: nop + IL_0011: ret } "); } [Fact] - public void PropertyAccess_CompoundAssignment_04() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void PropertyAccess_Set_04() { var src = """ using System.Threading.Tasks; @@ -5946,6 +5989,8 @@ class Program class Program { +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; @@ -5958,21 +6003,22 @@ static async Task Main() Test2(ref Program.F); System.Console.Write(Program.F.F1); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - f.P1 += Get1(); + f.P1 = Get1(); } static void Test2(ref T f) where T : struct { - f.P1 += Get1(); + f.P1 = Get1(); } static int Get1() @@ -5981,38 +6027,39 @@ static int Get1() return 1; } - static async Task Test3() - { - Program.F.P1 += await Get1Async(); - } + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F.P1 = await Get1Async(); + //} - static async Task Get1Async() - { - Program.F.F1++; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 62 (0x3e) - .maxstack 3 + // Code size 50 (0x32) + .maxstack 2 .locals init (T V_0, T& V_1, - T V_2, - int V_3) + int V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 - IL_0003: ldloca.s V_2 + IL_0003: ldloca.s V_3 IL_0005: initobj ""T"" - IL_000b: ldloc.2 + IL_000b: ldloc.3 IL_000c: box ""T"" IL_0011: brtrue.s IL_001e IL_0013: ldloc.1 @@ -6021,82 +6068,125 @@ .locals init (T V_0, IL_001a: ldloca.s V_0 IL_001c: br.s IL_001f IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""int E.get_P1(T)"" - IL_002a: call ""int Program.Get1()"" - IL_002f: add - IL_0030: stloc.3 - IL_0031: ldobj ""T"" - IL_0036: ldloc.3 - IL_0037: call ""void E.set_P1(T, int)"" - IL_003c: nop - IL_003d: ret + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 33 (0x21) - .maxstack 3 + // Code size 21 (0x15) + .maxstack 2 .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: dup - IL_0003: ldobj ""T"" - IL_0008: call ""int E.get_P1(T)"" - IL_000d: call ""int Program.Get1()"" - IL_0012: add - IL_0013: stloc.0 - IL_0014: ldobj ""T"" - IL_0019: ldloc.0 - IL_001a: call ""void E.set_P1(T, int)"" - IL_001f: nop - IL_0020: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""T"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(T, int)"" + IL_0013: nop + IL_0014: ret } "); - } - - [Fact] - public void PropertyAccess_CompoundAssignment_05() - { - var src = """ -using System.Threading.Tasks; + var src2 = """ static class E { - extension(ref T x) where T : struct + extension(T x) where T : struct { - public int P1 - { - get - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return 0; - } - set - { - System.Console.Write(((S1)(object)x).F1); - } - } + public int P1 { get => 0; set {} } } } -struct S1 +class Program { - public int F1; + static void Test() where T : struct + { + default(T).P1 = 1; + } } -class Program +namespace NS1 { - public static T F; + static class E + { + extension(in T x) where T : struct + { + } + } } -class Program +namespace NS2 { - static async Task Main() + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation([src2]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T).P1 = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T).P1").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void PropertyAccess_Set_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public int P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() { Program.F = new S1 { F1 = 123 }; Test2(ref Program.F); @@ -6111,7 +6201,7 @@ static async Task Main() static void Test2(ref T f) where T : struct { - f.P1 += Get1(); + f.P1 = Get1(); } static int Get1() @@ -6122,7 +6212,7 @@ static int Get1() static async Task Test3() where T : struct { - Program.F.P1 += await Get1Async(); + Program.F.P1 = await Get1Async(); } static async Task Get1Async() @@ -6134,23 +6224,20 @@ static async Task Get1Async() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 21 (0x15) - .maxstack 3 + // Code size 14 (0xe) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: dup - IL_0003: call ""int E.get_P1(ref T)"" - IL_0008: call ""int Program.Get1()"" - IL_000d: add - IL_000e: call ""void E.set_P1(ref T, int)"" - IL_0013: nop - IL_0014: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""void E.set_P1(ref T, int)"" + IL_000c: nop + IL_000d: ret } "); @@ -6167,7 +6254,7 @@ class Program { static void Test() where T : struct { - default(T).P1 += 1; + default(T).P1 = 1; } } @@ -6192,10 +6279,10 @@ static class E } """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2]); comp2.VerifyDiagnostics( // (13,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P1 += 1; + // default(T).P1 = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension(in T x) where T : struct @@ -6207,7 +6294,8 @@ static class E } [Fact] - public void PropertyAccess_CompoundAssignment_06() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void PropertyAccess_Set_06() { var src = """ using System.Threading.Tasks; @@ -6244,6 +6332,8 @@ class Program class Program { +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new C1 { F1 = 123 }; @@ -6256,21 +6346,22 @@ static async Task Main() Test2(ref Program.F); System.Console.Write(Program.F.F1); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - f.P1 += Get1(); + f.P1 = Get1(); } static void Test2(ref T f) where T : class { - f.P1 += Get1(); + f.P1 = Get1(); } static int Get1() @@ -6279,38 +6370,39 @@ static int Get1() return 1; } - static async Task Test3() - { - Program.F.P1 += await Get1Async(); - } + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F.P1 = await Get1Async(); + //} - static async Task Get1Async() - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 62 (0x3e) - .maxstack 3 + // Code size 50 (0x32) + .maxstack 2 .locals init (T V_0, T& V_1, - T V_2, - int V_3) + int V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 - IL_0003: ldloca.s V_2 + IL_0003: ldloca.s V_3 IL_0005: initobj ""T"" - IL_000b: ldloc.2 + IL_000b: ldloc.3 IL_000c: box ""T"" IL_0011: brtrue.s IL_001e IL_0013: ldloc.1 @@ -6319,58 +6411,52 @@ .locals init (T V_0, IL_001a: ldloca.s V_0 IL_001c: br.s IL_001f IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""int E.get_P1(T)"" - IL_002a: call ""int Program.Get1()"" - IL_002f: add - IL_0030: stloc.3 - IL_0031: ldobj ""T"" - IL_0036: ldloc.3 - IL_0037: call ""void E.set_P1(T, int)"" - IL_003c: nop - IL_003d: ret + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 26 (0x1a) - .maxstack 3 + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj ""T"" - IL_0007: dup - IL_0008: call ""int E.get_P1(T)"" - IL_000d: call ""int Program.Get1()"" - IL_0012: add - IL_0013: call ""void E.set_P1(T, int)"" - IL_0018: nop - IL_0019: ret + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_P1(T, int)"" + IL_0011: nop + IL_0012: ret } "); } [Fact] - public void PropertyAccess_PrefixIncrementAssignment_01() + public void PropertyAccess_Set_07() { var src = """ +using System.Threading.Tasks; + static class E { - extension(S1 x) + extension(T x) { - public S2 P1 + public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; - return default; + System.Console.Write(((S1)(object)x).F1); + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -6379,348 +6465,317 @@ public S2 P1 struct S1 { public int F1; - - public void Test() - { - ++this.P1; - } } -struct S2 +class Program { - public static S2 operator ++(S2 x) + static async Task Main() { - Program.F.F1++; - return x; + Test1(); + + System.Console.Write(":"); + + await Test3(); } -} -class Program -{ - public static S1 F; + static T GetT() => (T)(object)new S1 { F1 = 123 }; - static void Main() + static void Test1() { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + GetT().P1 = Get1(); + } - System.Console.Write(":"); + static int Get1() + { + return 1; + } - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + static async Task Test3() + { + GetT().P1 = await Get1Async(); } - static void Test() + static async Task Get1Async() { - ++F.P1; + await Task.Yield(); + return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 36 (0x24) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: ldobj ""S1"" - IL_000c: call ""S2 E.get_P1(S1)"" - IL_0011: call ""S2 S2.op_Increment(S2)"" - IL_0016: stloc.0 - IL_0017: ldobj ""S1"" - IL_001c: ldloc.0 - IL_001d: call ""void E.set_P1(S1, S2)"" - IL_0022: nop - IL_0023: ret -} -"); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123").VerifyDiagnostics(); - verifier.VerifyIL("S1.Test", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 32 (0x20) + // Code size 18 (0x12) .maxstack 2 - .locals init (S2 V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""S2 E.get_P1(S1)"" - IL_000c: call ""S2 S2.op_Increment(S2)"" - IL_0011: stloc.0 - IL_0012: ldarg.0 - IL_0013: ldobj ""S1"" - IL_0018: ldloc.0 - IL_0019: call ""void E.set_P1(S1, S2)"" - IL_001e: nop - IL_001f: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(T, int)"" + IL_0010: nop + IL_0011: ret } "); } - [Theory] - [InlineData("ref")] - [InlineData("ref readonly")] - [InlineData("in")] - public void PropertyAccess_PrefixIncrementAssignment_02(string refKind) + [Fact] + public void PropertyAccess_Set_08() { - var src = $$$""" + var src = """ +using System.Threading.Tasks; + static class E { - extension({{{refKind}}} S1 x) + extension(T x) { - public S2 P1 + public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; - return default; + System.Console.Write(((C1)(object)x).F1); + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((C1)(object)x).F1); } } } } -struct S1 +class C1 { public int F1; - - public void Test() - { - ++this.P1; - } } -struct S2 +class Program { - public static S2 operator ++(S2 x) + static async Task Main() { - Program.F.F1++; - return x; + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); } -} -class Program -{ - public static S1 F; + static T GetT() => (T)(object)new C1 { F1 = 123 }; - static void Main() + static void Test1() { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + GetT().P1 = Get1(); + } - System.Console.Write(":"); + static void Test2() where T : class + { + GetT().P1 = Get1(); + } - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + static int Get1() + { + return 1; } - static void Test() + static async Task Test3() { - ++F.P1; + GetT().P1 = await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123:123").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 26 (0x1a) + // Code size 18 (0x12) .maxstack 2 - .locals init (S2 V_0) IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: call ""S2 E.get_P1(" + refKind + @" S1)"" - IL_000c: call ""S2 S2.op_Increment(S2)"" - IL_0011: stloc.0 - IL_0012: ldloc.0 - IL_0013: call ""void E.set_P1(" + refKind + @" S1, S2)"" - IL_0018: nop - IL_0019: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(T, int)"" + IL_0010: nop + IL_0011: ret } "); - verifier.VerifyIL("S1.Test", + verifier.VerifyIL("Program.Test2()", @" { - // Code size 22 (0x16) + // Code size 18 (0x12) .maxstack 2 - .locals init (S2 V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""S2 E.get_P1(" + refKind + @" S1)"" - IL_0007: call ""S2 S2.op_Increment(S2)"" - IL_000c: stloc.0 - IL_000d: ldarg.0 - IL_000e: ldloc.0 - IL_000f: call ""void E.set_P1(" + refKind + @" S1, S2)"" - IL_0014: nop - IL_0015: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(T, int)"" + IL_0010: nop + IL_0011: ret } "); - - var src2 = $$$""" -static class E -{ - extension({{{refKind}}} S1 x) - { - public int P1 { get => 0; set {} } - } -} - -struct S1; - -class Program -{ - static void Test() - { - ++default(S1).P1; - } -} -"""; - - var comp2 = CreateCompilation(src2); - switch (refKind) - { - case "ref": - comp2.VerifyDiagnostics( - // (15,11): error CS1510: A ref or out value must be an assignable variable - // ++default(S1).P1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 11) - ); - break; - case "ref readonly": - comp2.VerifyDiagnostics( - // (15,11): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // ++default(S1).P1; - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 11), - // (15,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // ++default(S1).P1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 11) - ); - break; - case "in": - comp2.VerifyDiagnostics( - // (15,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // ++default(S1).P1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 11) - ); - break; - default: - throw ExceptionUtilities.UnexpectedValue(refKind); - } } [Fact] - public void PropertyAccess_PrefixIncrementAssignment_03() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void PropertyAccess_Set_ReadonlyReceiver_040() { var src = """ +using System.Threading.Tasks; + static class E { - extension(C1 x) + extension(T x) { - public S2 P1 + public int P1 { get { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return default; + System.Console.Write(((S1)(object)x).F1); + Program.Increment(); + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } } -class C1 +struct S1 { public int F1; } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return x; - } + public static readonly T F; } class Program { - public static C1 F = new C1 { F1 = 123 }; + static async Task Main() + { + Initialize(); + Test1(); + System.Console.Write(Program.F.F1); - static void Main() + System.Console.Write(":"); + + Initialize(); + await Test3(); + System.Console.Write(Program.F.F1); + } + + static unsafe void Initialize() { - Test(); - System.Console.Write(F.F1); + fixed (int* f1 = &Program.F.F1) + { + *f1 = 123; + } } - static void Test() + public static unsafe void Increment() { - ++F.P1; + fixed (int* f1 = &Program.F.F1) + { + (*f1)++; + } + } + + static void Test1() + { + Program.F.P1 = Get1(); + } + + static int Get1() + { + Increment(); + return 1; + } + + static async Task Test3() + { + Program.F.P1 = await Get1Async(); + } + + static async Task Get1Async() + { + Increment(); + await Task.Yield(); + return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124", verify: Verification.Skipped).VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 26 (0x1a) + // Code size 54 (0x36) .maxstack 2 - .locals init (S2 V_0) + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: dup - IL_0007: call ""S2 E.get_P1(C1)"" - IL_000c: call ""S2 S2.op_Increment(S2)"" - IL_0011: stloc.0 - IL_0012: ldloc.0 - IL_0013: call ""void E.set_P1(C1, S2)"" - IL_0018: nop - IL_0019: ret + IL_0001: ldsflda ""T Program.F"" + IL_0006: stloc.1 + IL_0007: ldloca.s V_3 + IL_0009: initobj ""T"" + IL_000f: ldloc.3 + IL_0010: box ""T"" + IL_0015: brtrue.s IL_0022 + IL_0017: ldloc.1 + IL_0018: ldobj ""T"" + IL_001d: stloc.0 + IL_001e: ldloca.s V_0 + IL_0020: br.s IL_0023 + IL_0022: ldloc.1 + IL_0023: call ""int Program.Get1()"" + IL_0028: stloc.2 + IL_0029: ldobj ""T"" + IL_002e: ldloc.2 + IL_002f: call ""void E.set_P1(T, int)"" + IL_0034: nop + IL_0035: ret } "); } - [Fact] - public void PropertyAccess_PrefixIncrementAssignment_04() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_Set_ReadonlyReceiver_041(string refKind) { - var src = """ + var src = $$$""" static class E { extension(T x) { - public S2 P1 + public int P1 { get { System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return default; + Program.F.F1++; + return 0; } set { @@ -6735,55 +6790,44 @@ struct S1 public int F1; } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F.F1++; - return x; - } + public static T F; } class Program { - public static S1 F; - static void Main() { - F = new S1 { F1 = 123 }; - Test1(ref F); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - Test2(ref F); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program.F); + System.Console.Write(Program.F.F1); } - static void Test1(ref T f) + static void Test1({{{refKind}}} T f) { - ++f.P1; + f.P1 = Get1(); } - static void Test2(ref T f) where T : struct + static int Get1() { - ++f.P1; + Program.F.F1++; + return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL($"Program.Test1({refKind} T)", @" { - // Code size 61 (0x3d) + // Code size 50 (0x32) .maxstack 2 .locals init (T V_0, T& V_1, - S2 V_2, + int V_2, T V_3) IL_0000: nop IL_0001: ldarg.0 @@ -6799,186 +6843,35 @@ .locals init (T V_0, IL_001a: ldloca.s V_0 IL_001c: br.s IL_001f IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""S2 E.get_P1(T)"" - IL_002a: call ""S2 S2.op_Increment(S2)"" - IL_002f: stloc.2 - IL_0030: ldobj ""T"" - IL_0035: ldloc.2 - IL_0036: call ""void E.set_P1(T, S2)"" - IL_003b: nop - IL_003c: ret -} -"); - - verifier.VerifyIL("Program.Test2(ref T)", -@" -{ - // Code size 32 (0x20) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: dup - IL_0003: ldobj ""T"" - IL_0008: call ""S2 E.get_P1(T)"" - IL_000d: call ""S2 S2.op_Increment(S2)"" - IL_0012: stloc.0 - IL_0013: ldobj ""T"" - IL_0018: ldloc.0 - IL_0019: call ""void E.set_P1(T, S2)"" - IL_001e: nop - IL_001f: ret -} -"); - } - - [Fact] - public void PropertyAccess_PrefixIncrementAssignment_05() - { - var src = """ -static class E -{ - extension(ref T x) where T : struct - { - public S2 P1 - { - get - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return default; - } - set - { - System.Console.Write(((S1)(object)x).F1); - } - } - } -} - -struct S1 -{ - public int F1; -} - -struct S2 -{ - public static S2 operator ++(S2 x) - { - Program.F.F1++; - return x; - } -} - -class Program -{ - public static S1 F; - - static void Main() - { - F = new S1 { F1 = 123 }; - Test2(ref F); - System.Console.Write(F.F1); - } - - static void Test2(ref T f) where T : struct - { - ++f.P1; - } -} -"""; - - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test2(ref T)", -@" -{ - // Code size 22 (0x16) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: dup - IL_0003: call ""S2 E.get_P1(ref T)"" - IL_0008: call ""S2 S2.op_Increment(S2)"" - IL_000d: stloc.0 - IL_000e: ldloc.0 - IL_000f: call ""void E.set_P1(ref T, S2)"" - IL_0014: nop - IL_0015: ret + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret } "); - - var src2 = """ -static class E -{ - extension(ref T x) where T : struct - { - public int P1 { get => 0; set {} } - } -} - -class Program -{ - static void Test() where T : struct - { - ++default(T).P1; - } -} - -namespace NS1 -{ - static class E - { - extension(in T x) where T : struct - { - } - } -} - -namespace NS2 -{ - static class E - { - extension(ref readonly T x) where T : struct - { - } - } -} -"""; - - var comp2 = CreateCompilation(src2); - comp2.VerifyDiagnostics( - // (13,11): error CS1510: A ref or out value must be an assignable variable - // ++default(T).P1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 11), - // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), - // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) - ); } - [Fact] - public void PropertyAccess_PrefixIncrementAssignment_06() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_Set_ReadonlyReceiver_061(string refKind) { - var src = """ + var src = $$$""" static class E { extension(T x) { - public S2 P1 + public int P1 { get { System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return default; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; } set { @@ -6993,55 +6886,44 @@ class C1 public int F1; } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return x; - } + public static T F; } class Program { - public static C1 F; - static void Main() { - F = new C1 { F1 = 123 }; - Test1(ref F); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new C1 { F1 = 123 }; - Test2(ref F); - System.Console.Write(F.F1); + Program.F = new C1 { F1 = 123 }; + Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program.F); + System.Console.Write(Program.F.F1); } - static void Test1(ref T f) + static void Test1({{{refKind}}} T f) { - ++f.P1; + f.P1 = Get1(); } - static void Test2(ref T f) where T : class + static int Get1() { - ++f.P1; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL($"Program.Test1({refKind} T)", @" { - // Code size 61 (0x3d) + // Code size 50 (0x32) .maxstack 2 .locals init (T V_0, T& V_1, - S2 V_2, + int V_2, T V_3) IL_0000: nop IL_0001: ldarg.0 @@ -7057,55 +6939,32 @@ .locals init (T V_0, IL_001a: ldloca.s V_0 IL_001c: br.s IL_001f IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""S2 E.get_P1(T)"" - IL_002a: call ""S2 S2.op_Increment(S2)"" - IL_002f: stloc.2 - IL_0030: ldobj ""T"" - IL_0035: ldloc.2 - IL_0036: call ""void E.set_P1(T, S2)"" - IL_003b: nop - IL_003c: ret -} -"); - - verifier.VerifyIL("Program.Test2(ref T)", -@" -{ - // Code size 27 (0x1b) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: dup - IL_0008: call ""S2 E.get_P1(T)"" - IL_000d: call ""S2 S2.op_Increment(S2)"" - IL_0012: stloc.0 - IL_0013: ldloc.0 - IL_0014: call ""void E.set_P1(T, S2)"" - IL_0019: nop - IL_001a: ret + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret } "); } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_01() + public void PropertyAccess_CompoundAssignment_01() { var src = """ static class E { extension(S1 x) { - public S2 P1 + public int P1 { get { System.Console.Write(x.F1); Program.F.F1++; - return default; + return 0; } set { @@ -7121,16 +6980,7 @@ struct S1 public void Test() { - this.P1++; - } -} - -struct S2 -{ - public static S2 operator ++(S2 x) - { - Program.F.F1++; - return x; + this.P1 += Program.Get1(); } } @@ -7153,7 +7003,13 @@ static void Main() static void Test() { - F.P1++; + F.P1 += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; } } """; @@ -7164,42 +7020,44 @@ static void Test() verifier.VerifyIL("Program.Test", @" { - // Code size 36 (0x24) - .maxstack 2 - .locals init (S2 V_0) + // Code size 37 (0x25) + .maxstack 3 + .locals init (int V_0) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" IL_0006: dup IL_0007: ldobj ""S1"" - IL_000c: call ""S2 E.get_P1(S1)"" - IL_0011: call ""S2 S2.op_Increment(S2)"" - IL_0016: stloc.0 - IL_0017: ldobj ""S1"" - IL_001c: ldloc.0 - IL_001d: call ""void E.set_P1(S1, S2)"" - IL_0022: nop - IL_0023: ret + IL_000c: call ""int E.get_P1(S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: add + IL_0017: stloc.0 + IL_0018: ldobj ""S1"" + IL_001d: ldloc.0 + IL_001e: call ""void E.set_P1(S1, int)"" + IL_0023: nop + IL_0024: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 32 (0x20) + // Code size 33 (0x21) .maxstack 2 - .locals init (S2 V_0) + .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj ""S1"" - IL_0007: call ""S2 E.get_P1(S1)"" - IL_000c: call ""S2 S2.op_Increment(S2)"" - IL_0011: stloc.0 - IL_0012: ldarg.0 - IL_0013: ldobj ""S1"" - IL_0018: ldloc.0 - IL_0019: call ""void E.set_P1(S1, S2)"" - IL_001e: nop - IL_001f: ret + IL_0007: call ""int E.get_P1(S1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: stloc.0 + IL_0013: ldarg.0 + IL_0014: ldobj ""S1"" + IL_0019: ldloc.0 + IL_001a: call ""void E.set_P1(S1, int)"" + IL_001f: nop + IL_0020: ret } "); } @@ -7208,20 +7066,20 @@ .locals init (S2 V_0) [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void PropertyAccess_PostfixIncrementAssignment_02(string refKind) + public void PropertyAccess_CompoundAssignment_02(string refKind) { var src = $$$""" static class E { extension({{{refKind}}} S1 x) { - public S2 P1 + public int P1 { get { System.Console.Write(x.F1); Program.F.F1++; - return default; + return 0; } set { @@ -7237,16 +7095,7 @@ struct S1 public void Test() { - this.P1++; - } -} - -struct S2 -{ - public static S2 operator ++(S2 x) - { - Program.F.F1++; - return x; + this.P1 += Program.Get1(); } } @@ -7269,7 +7118,13 @@ static void Main() static void Test() { - F.P1++; + F.P1 += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; } } """; @@ -7280,38 +7135,34 @@ static void Test() verifier.VerifyIL("Program.Test", @" { - // Code size 26 (0x1a) - .maxstack 2 - .locals init (S2 V_0) + // Code size 25 (0x19) + .maxstack 3 IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" IL_0006: dup - IL_0007: call ""S2 E.get_P1(" + refKind + @" S1)"" - IL_000c: stloc.0 - IL_000d: ldloc.0 - IL_000e: call ""S2 S2.op_Increment(S2)"" - IL_0013: call ""void E.set_P1(" + refKind + @" S1, S2)"" - IL_0018: nop - IL_0019: ret + IL_0007: call ""int E.get_P1(" + refKind + @" S1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0017: nop + IL_0018: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 22 (0x16) - .maxstack 2 - .locals init (S2 V_0) + // Code size 21 (0x15) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 - IL_0002: call ""S2 E.get_P1(" + refKind + @" S1)"" - IL_0007: stloc.0 - IL_0008: ldarg.0 - IL_0009: ldloc.0 - IL_000a: call ""S2 S2.op_Increment(S2)"" - IL_000f: call ""void E.set_P1(" + refKind + @" S1, S2)"" - IL_0014: nop - IL_0015: ret + IL_0002: ldarg.0 + IL_0003: call ""int E.get_P1(" + refKind + @" S1)"" + IL_0008: call ""int Program.Get1()"" + IL_000d: add + IL_000e: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0013: nop + IL_0014: ret } "); @@ -7330,7 +7181,7 @@ class Program { static void Test() { - default(S1).P1++; + default(S1).P1 += 1; } } """; @@ -7341,24 +7192,24 @@ static void Test() case "ref": comp2.VerifyDiagnostics( // (15,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P1++; + // default(S1).P1 += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) ); break; case "ref readonly": comp2.VerifyDiagnostics( // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P1++; + // default(S1).P1 += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1++; + // default(S1).P1 += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) ); break; case "in": comp2.VerifyDiagnostics( // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1++; + // default(S1).P1 += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) ); break; @@ -7368,20 +7219,20 @@ static void Test() } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_03() + public void PropertyAccess_CompoundAssignment_03() { var src = """ static class E { extension(C1 x) { - public S2 P1 + public int P1 { get { System.Console.Write(x.F1); Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return default; + return 0; } set { @@ -7396,28 +7247,26 @@ class C1 public int F1; } -struct S2 -{ - public static S2 operator ++(S2 x) - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return x; - } -} - class Program { - public static C1 F = new C1 { F1 = 123 }; + public static C1 F; static void Main() { + F = new C1 { F1 = 123 }; Test(); System.Console.Write(F.F1); } static void Test() { - F.P1++; + F.P1 += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; } } """; @@ -7428,38 +7277,38 @@ static void Test() verifier.VerifyIL("Program.Test", @" { - // Code size 26 (0x1a) - .maxstack 2 - .locals init (S2 V_0) + // Code size 25 (0x19) + .maxstack 3 IL_0000: nop IL_0001: ldsfld ""C1 Program.F"" IL_0006: dup - IL_0007: call ""S2 E.get_P1(C1)"" - IL_000c: stloc.0 - IL_000d: ldloc.0 - IL_000e: call ""S2 S2.op_Increment(S2)"" - IL_0013: call ""void E.set_P1(C1, S2)"" - IL_0018: nop - IL_0019: ret + IL_0007: call ""int E.get_P1(C1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_P1(C1, int)"" + IL_0017: nop + IL_0018: ret } "); } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_04() + public void PropertyAccess_CompoundAssignment_04() { var src = """ +using System.Threading.Tasks; + static class E { extension(T x) { - public S2 P1 + public int P1 { get { System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return default; + Program.F.F1++; + return 0; } set { @@ -7474,62 +7323,80 @@ struct S1 public int F1; } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F.F1++; - return x; - } + public static T F; } class Program { - public static S1 F; - - static void Main() + static async Task Main() { - F = new S1 { F1 = 123 }; - Test1(ref F); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - F = new S1 { F1 = 123 }; - Test2(ref F); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - f.P1++; + f.P1 += Get1(); } static void Test2(ref T f) where T : struct { - f.P1++; + f.P1 += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + Program.F.P1 += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 61 (0x3d) - .maxstack 2 + // Code size 62 (0x3e) + .maxstack 3 .locals init (T V_0, T& V_1, - S2 V_2, - T V_3) + T V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 - IL_0003: ldloca.s V_3 + IL_0003: ldloca.s V_2 IL_0005: initobj ""T"" - IL_000b: ldloc.3 + IL_000b: ldloc.2 IL_000c: box ""T"" IL_0011: brtrue.s IL_001e IL_0013: ldloc.1 @@ -7540,54 +7407,58 @@ .locals init (T V_0, IL_001e: ldloc.1 IL_001f: dup IL_0020: ldobj ""T"" - IL_0025: call ""S2 E.get_P1(T)"" - IL_002a: call ""S2 S2.op_Increment(S2)"" - IL_002f: stloc.2 - IL_0030: ldobj ""T"" - IL_0035: ldloc.2 - IL_0036: call ""void E.set_P1(T, S2)"" - IL_003b: nop - IL_003c: ret -} -"); - + IL_0025: call ""int E.get_P1(T)"" + IL_002a: call ""int Program.Get1()"" + IL_002f: add + IL_0030: stloc.3 + IL_0031: ldobj ""T"" + IL_0036: ldloc.3 + IL_0037: call ""void E.set_P1(T, int)"" + IL_003c: nop + IL_003d: ret +} +"); + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 32 (0x20) - .maxstack 2 - .locals init (S2 V_0) + // Code size 33 (0x21) + .maxstack 3 + .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: dup IL_0003: ldobj ""T"" - IL_0008: call ""S2 E.get_P1(T)"" - IL_000d: call ""S2 S2.op_Increment(S2)"" - IL_0012: stloc.0 - IL_0013: ldobj ""T"" - IL_0018: ldloc.0 - IL_0019: call ""void E.set_P1(T, S2)"" - IL_001e: nop - IL_001f: ret + IL_0008: call ""int E.get_P1(T)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldobj ""T"" + IL_0019: ldloc.0 + IL_001a: call ""void E.set_P1(T, int)"" + IL_001f: nop + IL_0020: ret } "); } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_05() + public void PropertyAccess_CompoundAssignment_05() { var src = """ +using System.Threading.Tasks; + static class E { extension(ref T x) where T : struct { - public S2 P1 + public int P1 { get { System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return default; + Program.F.F1++; + return 0; } set { @@ -7602,52 +7473,68 @@ struct S1 public int F1; } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F.F1++; - return x; - } + public static T F; } class Program { - public static S1 F; - - static void Main() + static async Task Main() { - F = new S1 { F1 = 123 }; - Test2(ref F); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } static void Test2(ref T f) where T : struct { - f.P1++; + f.P1 += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F.P1 += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 22 (0x16) - .maxstack 2 - .locals init (S2 V_0) + // Code size 21 (0x15) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 IL_0002: dup - IL_0003: call ""S2 E.get_P1(ref T)"" - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: call ""S2 S2.op_Increment(S2)"" - IL_000f: call ""void E.set_P1(ref T, S2)"" - IL_0014: nop - IL_0015: ret + IL_0003: call ""int E.get_P1(ref T)"" + IL_0008: call ""int Program.Get1()"" + IL_000d: add + IL_000e: call ""void E.set_P1(ref T, int)"" + IL_0013: nop + IL_0014: ret } "); @@ -7664,7 +7551,7 @@ class Program { static void Test() where T : struct { - default(T).P1++; + default(T).P1 += 1; } } @@ -7692,7 +7579,7 @@ static class E var comp2 = CreateCompilation(src2); comp2.VerifyDiagnostics( // (13,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P1++; + // default(T).P1 += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension(in T x) where T : struct @@ -7704,20 +7591,22 @@ static class E } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_06() + public void PropertyAccess_CompoundAssignment_06() { var src = """ +using System.Threading.Tasks; + static class E { extension(T x) { - public S2 P1 + public int P1 { get { System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return default; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; } set { @@ -7732,62 +7621,80 @@ class C1 public int F1; } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return x; - } + public static T F; } class Program { - public static C1 F; - - static void Main() + static async Task Main() { - F = new C1 { F1 = 123 }; - Test1(ref F); - System.Console.Write(F.F1); + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - F = new C1 { F1 = 123 }; - Test2(ref F); - System.Console.Write(F.F1); + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - f.P1++; + f.P1 += Get1(); } static void Test2(ref T f) where T : class { - f.P1++; + f.P1 += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + Program.F.P1 += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 61 (0x3d) - .maxstack 2 + // Code size 62 (0x3e) + .maxstack 3 .locals init (T V_0, T& V_1, - S2 V_2, - T V_3) + T V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 - IL_0003: ldloca.s V_3 + IL_0003: ldloca.s V_2 IL_0005: initobj ""T"" - IL_000b: ldloc.3 + IL_000b: ldloc.2 IL_000c: box ""T"" IL_0011: brtrue.s IL_001e IL_0013: ldloc.1 @@ -7798,71 +7705,58 @@ .locals init (T V_0, IL_001e: ldloc.1 IL_001f: dup IL_0020: ldobj ""T"" - IL_0025: call ""S2 E.get_P1(T)"" - IL_002a: call ""S2 S2.op_Increment(S2)"" - IL_002f: stloc.2 - IL_0030: ldobj ""T"" - IL_0035: ldloc.2 - IL_0036: call ""void E.set_P1(T, S2)"" - IL_003b: nop - IL_003c: ret + IL_0025: call ""int E.get_P1(T)"" + IL_002a: call ""int Program.Get1()"" + IL_002f: add + IL_0030: stloc.3 + IL_0031: ldobj ""T"" + IL_0036: ldloc.3 + IL_0037: call ""void E.set_P1(T, int)"" + IL_003c: nop + IL_003d: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 27 (0x1b) - .maxstack 2 - .locals init (S2 V_0) + // Code size 26 (0x1a) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj ""T"" IL_0007: dup - IL_0008: call ""S2 E.get_P1(T)"" - IL_000d: stloc.0 - IL_000e: ldloc.0 - IL_000f: call ""S2 S2.op_Increment(S2)"" - IL_0014: call ""void E.set_P1(T, S2)"" - IL_0019: nop - IL_001a: ret + IL_0008: call ""int E.get_P1(T)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: call ""void E.set_P1(T, int)"" + IL_0018: nop + IL_0019: ret } "); } [Fact] - public void PropertyAccess_ConditionalAssignment_01() + public void PropertyAccess_CompoundAssignment_ReadonlyReceiver_040() { var src = """ +using System.Threading.Tasks; + static class E { - extension(S1 x) + extension(T x) { - public object P1 - { - get - { - System.Console.Write(x.F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(x.F1); - } - } - - public int? P2 + public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; - return null; + System.Console.Write(((S1)(object)x).F1); + Program.Increment(); + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -7871,192 +7765,107 @@ public int? P2 struct S1 { public int F1; +} - public void Test1() - { - this.P1 ??= Program.Get1(); - } - - public void Test2() - { - this.P2 ??= Program.Get1(); - } +class Program +{ + public static readonly T F; } class Program { - public static S1 F; - - static void Main() + static async Task Main() { - F = new S1 { F1 = 123 }; - Test1(); - System.Console.Write(F.F1); + Initialize(); + Test1(); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - F = new S1 { F1 = 123 }; - F.Test1(); - System.Console.Write(F.F1); - - System.Console.Write(":"); + Initialize(); + await Test3(); + System.Console.Write(Program.F.F1); + } - F = new S1 { F1 = 123 }; - Test2(); - System.Console.Write(F.F1); + static unsafe void Initialize() + { + fixed (int* f1 = &Program.F.F1) + { + *f1 = 123; + } + } - System.Console.Write(":"); + public static unsafe void Increment() + { + fixed (int* f1 = &Program.F.F1) + { + (*f1)++; + } + } - F = new S1 { F1 = 123 }; - F.Test2(); - System.Console.Write(F.F1); + static void Test1() + { + Program.F.P1 += Get1(); } - static void Test1() + static int Get1() { - F.P1 ??= Get1(); + Increment(); + return 1; } - static void Test2() + static async Task Test3() { - F.P2 ??= Get1(); + Program.F.P1 += await Get1Async(); } - public static int Get1() + static async Task Get1Async() { - Program.F.F1++; + Increment(); + await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test1", -@" -{ - // Code size 47 (0x2f) - .maxstack 3 - .locals init (S1& V_0, - object V_1, - object V_2) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldobj ""S1"" - IL_000d: call ""object E.get_P1(S1)"" - IL_0012: brtrue.s IL_002e - IL_0014: call ""int Program.Get1()"" - IL_0019: box ""int"" - IL_001e: stloc.1 - IL_001f: ldloc.0 - IL_0020: ldobj ""S1"" - IL_0025: ldloc.1 - IL_0026: dup - IL_0027: stloc.2 - IL_0028: call ""void E.set_P1(S1, object)"" - IL_002d: nop - IL_002e: ret -} -"); - - verifier.VerifyIL("S1.Test1", -@" -{ - // Code size 41 (0x29) - .maxstack 3 - .locals init (object V_0, - object V_1) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""object E.get_P1(S1)"" - IL_000c: brtrue.s IL_0028 - IL_000e: call ""int Program.Get1()"" - IL_0013: box ""int"" - IL_0018: stloc.0 - IL_0019: ldarg.0 - IL_001a: ldobj ""S1"" - IL_001f: ldloc.0 - IL_0020: dup - IL_0021: stloc.1 - IL_0022: call ""void E.set_P1(S1, object)"" - IL_0027: nop - IL_0028: ret -} -"); + var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125", verify: Verification.Skipped).VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2", + verifier.VerifyIL("Program.Test1()", @" { // Code size 66 (0x42) .maxstack 3 - .locals init (S1& V_0, - int? V_1, - int V_2, - int? V_3) + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldobj ""S1"" - IL_000d: call ""int? E.get_P2(S1)"" - IL_0012: stloc.1 - IL_0013: ldloca.s V_1 - IL_0015: call ""int int?.GetValueOrDefault()"" - IL_001a: stloc.2 - IL_001b: ldloca.s V_1 - IL_001d: call ""bool int?.HasValue.get"" - IL_0022: brtrue.s IL_0041 - IL_0024: call ""int Program.Get1()"" - IL_0029: stloc.2 - IL_002a: ldloc.0 - IL_002b: ldobj ""S1"" - IL_0030: ldloca.s V_3 - IL_0032: ldloc.2 - IL_0033: call ""int?..ctor(int)"" - IL_0038: ldloc.3 - IL_0039: call ""void E.set_P2(S1, int?)"" - IL_003e: nop - IL_003f: br.s IL_0041 + IL_0001: ldsflda ""T Program.F"" + IL_0006: stloc.1 + IL_0007: ldloca.s V_2 + IL_0009: initobj ""T"" + IL_000f: ldloc.2 + IL_0010: box ""T"" + IL_0015: brtrue.s IL_0022 + IL_0017: ldloc.1 + IL_0018: ldobj ""T"" + IL_001d: stloc.0 + IL_001e: ldloca.s V_0 + IL_0020: br.s IL_0023 + IL_0022: ldloc.1 + IL_0023: dup + IL_0024: ldobj ""T"" + IL_0029: call ""int E.get_P1(T)"" + IL_002e: call ""int Program.Get1()"" + IL_0033: add + IL_0034: stloc.3 + IL_0035: ldobj ""T"" + IL_003a: ldloc.3 + IL_003b: call ""void E.set_P1(T, int)"" + IL_0040: nop IL_0041: ret } -"); - - verifier.VerifyIL("S1.Test2", -@" -{ - // Code size 60 (0x3c) - .maxstack 3 - .locals init (int? V_0, - int V_1, - int? V_2) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""int? E.get_P2(S1)"" - IL_000c: stloc.0 - IL_000d: ldloca.s V_0 - IL_000f: call ""int int?.GetValueOrDefault()"" - IL_0014: stloc.1 - IL_0015: ldloca.s V_0 - IL_0017: call ""bool int?.HasValue.get"" - IL_001c: brtrue.s IL_003b - IL_001e: call ""int Program.Get1()"" - IL_0023: stloc.1 - IL_0024: ldarg.0 - IL_0025: ldobj ""S1"" - IL_002a: ldloca.s V_2 - IL_002c: ldloc.1 - IL_002d: call ""int?..ctor(int)"" - IL_0032: ldloc.2 - IL_0033: call ""void E.set_P2(S1, int?)"" - IL_0038: nop - IL_0039: br.s IL_003b - IL_003b: ret -} "); } @@ -8064,37 +7873,24 @@ .locals init (int? V_0, [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void PropertyAccess_ConditionalAssignment_02(string refKind) + public void PropertyAccess_CompoundAssignment_ReadonlyReceiver_041(string refKind) { var src = $$$""" static class E { - extension({{{refKind}}} S1 x) + extension(T x) { - public object P1 - { - get - { - System.Console.Write(x.F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(x.F1); - } - } - public int? P2 + public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; - return null; + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -8103,281 +7899,98 @@ public int? P2 struct S1 { public int F1; +} - public void Test1() - { - this.P1 ??= Program.Get1(); - } - - public void Test2() - { - this.P2 ??= Program.Get1(); - } +class Program +{ + public static T F; } class Program { - public static S1 F; - static void Main() { - F = new S1 { F1 = 123 }; - Test1(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test1(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - Test2(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test2(); - System.Console.Write(F.F1); - } - - static void Test1() - { - F.P1 ??= Get1(); + Program.F = new S1 { F1 = 123 }; + Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program.F); + System.Console.Write(Program.F.F1); } - static void Test2() + static void Test1({{{refKind}}} T f) { - F.P2 ??= Get1(); + f.P1 += Get1(); } - public static int Get1() + static int Get1() { - Program.F.F1++; + Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1", + verifier.VerifyIL($"Program.Test1({refKind} T)", @" { - // Code size 35 (0x23) + // Code size 62 (0x3e) .maxstack 3 - .locals init (S1& V_0, - object V_1) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""object E.get_P1(" + refKind + @" S1)"" - IL_000d: brtrue.s IL_0022 - IL_000f: ldloc.0 - IL_0010: call ""int Program.Get1()"" - IL_0015: box ""int"" - IL_001a: dup - IL_001b: stloc.1 - IL_001c: call ""void E.set_P1(" + refKind + @" S1, object)"" - IL_0021: nop - IL_0022: ret -} -"); - - verifier.VerifyIL("S1.Test1", -@" -{ - // Code size 29 (0x1d) - .maxstack 3 - .locals init (object V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""object E.get_P1(" + refKind + @" S1)"" - IL_0007: brtrue.s IL_001c - IL_0009: ldarg.0 - IL_000a: call ""int Program.Get1()"" - IL_000f: box ""int"" - IL_0014: dup - IL_0015: stloc.0 - IL_0016: call ""void E.set_P1(" + refKind + @" S1, object)"" - IL_001b: nop - IL_001c: ret -} -"); - - verifier.VerifyIL("Program.Test2", -@" -{ - // Code size 56 (0x38) - .maxstack 3 - .locals init (S1& V_0, - int? V_1, - int V_2, - int? V_3) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int? E.get_P2(" + refKind + @" S1)"" - IL_000d: stloc.1 - IL_000e: ldloca.s V_1 - IL_0010: call ""int int?.GetValueOrDefault()"" - IL_0015: stloc.2 - IL_0016: ldloca.s V_1 - IL_0018: call ""bool int?.HasValue.get"" - IL_001d: brtrue.s IL_0037 - IL_001f: call ""int Program.Get1()"" - IL_0024: stloc.2 - IL_0025: ldloc.0 - IL_0026: ldloca.s V_3 - IL_0028: ldloc.2 - IL_0029: call ""int?..ctor(int)"" - IL_002e: ldloc.3 - IL_002f: call ""void E.set_P2(" + refKind + @" S1, int?)"" - IL_0034: nop - IL_0035: br.s IL_0037 - IL_0037: ret -} -"); - - verifier.VerifyIL("S1.Test2", -@" -{ - // Code size 50 (0x32) - .maxstack 3 - .locals init (int? V_0, - int V_1, - int? V_2) + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: call ""int? E.get_P2(" + refKind + @" S1)"" - IL_0007: stloc.0 - IL_0008: ldloca.s V_0 - IL_000a: call ""int int?.GetValueOrDefault()"" - IL_000f: stloc.1 - IL_0010: ldloca.s V_0 - IL_0012: call ""bool int?.HasValue.get"" - IL_0017: brtrue.s IL_0031 - IL_0019: call ""int Program.Get1()"" - IL_001e: stloc.1 - IL_001f: ldarg.0 - IL_0020: ldloca.s V_2 - IL_0022: ldloc.1 - IL_0023: call ""int?..ctor(int)"" - IL_0028: ldloc.2 - IL_0029: call ""void E.set_P2(" + refKind + @" S1, int?)"" - IL_002e: nop - IL_002f: br.s IL_0031 - IL_0031: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""int E.get_P1(T)"" + IL_002a: call ""int Program.Get1()"" + IL_002f: add + IL_0030: stloc.3 + IL_0031: ldobj ""T"" + IL_0036: ldloc.3 + IL_0037: call ""void E.set_P1(T, int)"" + IL_003c: nop + IL_003d: ret } "); - - var src2 = $$$""" -static class E -{ - extension({{{refKind}}} S1 x) - { - public object P1 { get => 0; set {} } - public int? P2 { get => 0; set {} } - } -} - -struct S1; - -class Program -{ - static void Test1() - { - default(S1).P1 ??= 1; - } - static void Test2() - { - default(S1).P2 ??= 1; - } -} -"""; - - var comp2 = CreateCompilation(src2); - switch (refKind) - { - case "ref": - comp2.VerifyDiagnostics( - // (16,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P1 ??= 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), - // (20,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P2 ??= 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(20, 9) - ); - break; - case "ref readonly": - comp2.VerifyDiagnostics( - // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P1 ??= 1; - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), - // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 ??= 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(16, 9), - // (20,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P2 ??= 1; - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(20, 9), - // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P2 ??= 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P2").WithLocation(20, 9) - ); - break; - case "in": - comp2.VerifyDiagnostics( - // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 ??= 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(16, 9), - // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P2 ??= 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P2").WithLocation(20, 9) - ); - break; - default: - throw ExceptionUtilities.UnexpectedValue(refKind); - } } - [Fact] - public void PropertyAccess_ConditionalAssignment_03() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_CompoundAssignment_ReadonlyReceiver_061(string refKind) { - var src = """ + var src = $$$""" static class E { - extension(C1 x) + extension(T x) { - public object P1 - { - get - { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return null; - } - set - { - System.Console.Write(x.F1); - } - } - public int? P2 + public int P1 { get { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return null; + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((C1)(object)x).F1); } } } @@ -8388,138 +8001,93 @@ class C1 public int F1; } -class Program +class Program { - public static C1 F; + public static T F; +} +class Program +{ static void Main() { - F = new C1 { F1 = 123 }; - Test1(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new C1 { F1 = 123 }; - Test2(); - System.Console.Write(F.F1); - } - - static void Test1() - { - F.P1 ??= Get1(); + Program.F = new C1 { F1 = 123 }; + Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program.F); + System.Console.Write(Program.F.F1); } - static void Test2() + static void Test1({{{refKind}}} T f) { - F.P2 ??= Get1(); + f.P1 += Get1(); } static int Get1() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test1", -@" -{ - // Code size 35 (0x23) - .maxstack 3 - .locals init (C1 V_0, - object V_1) - IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""object E.get_P1(C1)"" - IL_000d: brtrue.s IL_0022 - IL_000f: ldloc.0 - IL_0010: call ""int Program.Get1()"" - IL_0015: box ""int"" - IL_001a: dup - IL_001b: stloc.1 - IL_001c: call ""void E.set_P1(C1, object)"" - IL_0021: nop - IL_0022: ret -} -"); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2", + verifier.VerifyIL($"Program.Test1({refKind} T)", @" { - // Code size 56 (0x38) + // Code size 62 (0x3e) .maxstack 3 - .locals init (C1 V_0, - int? V_1, - int V_2, - int? V_3) + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int? E.get_P2(C1)"" - IL_000d: stloc.1 - IL_000e: ldloca.s V_1 - IL_0010: call ""int int?.GetValueOrDefault()"" - IL_0015: stloc.2 - IL_0016: ldloca.s V_1 - IL_0018: call ""bool int?.HasValue.get"" - IL_001d: brtrue.s IL_0037 - IL_001f: call ""int Program.Get1()"" - IL_0024: stloc.2 - IL_0025: ldloc.0 - IL_0026: ldloca.s V_3 - IL_0028: ldloc.2 - IL_0029: call ""int?..ctor(int)"" - IL_002e: ldloc.3 - IL_002f: call ""void E.set_P2(C1, int?)"" - IL_0034: nop - IL_0035: br.s IL_0037 - IL_0037: ret + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""int E.get_P1(T)"" + IL_002a: call ""int Program.Get1()"" + IL_002f: add + IL_0030: stloc.3 + IL_0031: ldobj ""T"" + IL_0036: ldloc.3 + IL_0037: call ""void E.set_P1(T, int)"" + IL_003c: nop + IL_003d: ret } "); } [Fact] - public void PropertyAccess_ConditionalAssignment_04() + public void PropertyAccess_PrefixIncrementAssignment_01() { var src = """ -using System.Threading.Tasks; - static class E { - extension(T x) + extension(S1 x) { - public object P1 - { - get - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(((S1)(object)x).F1); - } - } - public int? P2 + public S2 P1 { get { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return null; + System.Console.Write(x.F1); + Program.F.F1++; + return default; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); } } } @@ -8528,298 +8096,10741 @@ public int? P2 struct S1 { public int F1; -} + public void Test() + { + ++this.P1; + } +} -class Program +struct S2 { - public static T F; + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } } class Program { - static async Task Main() - { - Program.F = new S1 { F1 = 123 }; - Test11(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - Test12(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); + public static S1 F; - Program.F = new S1 { F1 = 123 }; - await Test13(); - System.Console.Write(Program.F.F1); + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - Test21(ref Program.F); - System.Console.Write(Program.F.F1); + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } - System.Console.Write(":"); + static void Test() + { + ++F.P1; + } +} +"""; - Program.F = new S1 { F1 = 123 }; - Test22(ref Program.F); - System.Console.Write(Program.F.F1); + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - System.Console.Write(":"); + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 36 (0x24) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: ldobj ""S1"" + IL_000c: call ""S2 E.get_P1(S1)"" + IL_0011: call ""S2 S2.op_Increment(S2)"" + IL_0016: stloc.0 + IL_0017: ldobj ""S1"" + IL_001c: ldloc.0 + IL_001d: call ""void E.set_P1(S1, S2)"" + IL_0022: nop + IL_0023: ret +} +"); - Program.F = new S1 { F1 = 123 }; - await Test23(); - System.Console.Write(Program.F.F1); + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""S2 E.get_P1(S1)"" + IL_000c: call ""S2 S2.op_Increment(S2)"" + IL_0011: stloc.0 + IL_0012: ldarg.0 + IL_0013: ldobj ""S1"" + IL_0018: ldloc.0 + IL_0019: call ""void E.set_P1(S1, S2)"" + IL_001e: nop + IL_001f: ret +} +"); } - static void Test11(ref T f) + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_PrefixIncrementAssignment_02(string refKind) { - f.P1 ??= Get1(); - } - - static void Test21(ref T f) + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) { - f.P2 ??= Get1(); + public S2 P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(x.F1); + } + } } +} - static void Test12(ref T f) where T : struct - { - f.P1 ??= Get1(); - } +struct S1 +{ + public int F1; - static void Test22(ref T f) where T : struct + public void Test() { - f.P2 ??= Get1(); + ++this.P1; } +} - static int Get1() +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + ++F.P1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 26 (0x1a) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: call ""S2 E.get_P1(" + refKind + @" S1)"" + IL_000c: call ""S2 S2.op_Increment(S2)"" + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: call ""void E.set_P1(" + refKind + @" S1, S2)"" + IL_0018: nop + IL_0019: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 22 (0x16) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""S2 E.get_P1(" + refKind + @" S1)"" + IL_0007: call ""S2 S2.op_Increment(S2)"" + IL_000c: stloc.0 + IL_000d: ldarg.0 + IL_000e: ldloc.0 + IL_000f: call ""void E.set_P1(" + refKind + @" S1, S2)"" + IL_0014: nop + IL_0015: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int P1 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + ++default(S1).P1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (15,11): error CS1510: A ref or out value must be an assignable variable + // ++default(S1).P1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 11) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (15,11): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // ++default(S1).P1; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 11), + // (15,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // ++default(S1).P1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 11) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (15,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // ++default(S1).P1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 11) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } + } + + [Fact] + public void PropertyAccess_PrefixIncrementAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public S2 P1 + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return default; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return x; + } +} + +class Program +{ + public static C1 F = new C1 { F1 = 123 }; + + static void Main() + { + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + ++F.P1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 26 (0x1a) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: dup + IL_0007: call ""S2 E.get_P1(C1)"" + IL_000c: call ""S2 S2.op_Increment(S2)"" + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: call ""void E.set_P1(C1, S2)"" + IL_0018: nop + IL_0019: ret +} +"); + } + + [Fact] + public void PropertyAccess_PrefixIncrementAssignment_04() + { + var src = """ +static class E +{ + extension(T x) + { + public S2 P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test1(ref F); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test1(ref T f) + { + ++f.P1; + } + + static void Test2(ref T f) where T : struct + { + ++f.P1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T V_0, + T& V_1, + S2 V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""S2 E.get_P1(T)"" + IL_002a: call ""S2 S2.op_Increment(S2)"" + IL_002f: stloc.2 + IL_0030: ldobj ""T"" + IL_0035: ldloc.2 + IL_0036: call ""void E.set_P1(T, S2)"" + IL_003b: nop + IL_003c: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: call ""S2 E.get_P1(T)"" + IL_000d: call ""S2 S2.op_Increment(S2)"" + IL_0012: stloc.0 + IL_0013: ldobj ""T"" + IL_0018: ldloc.0 + IL_0019: call ""void E.set_P1(T, S2)"" + IL_001e: nop + IL_001f: ret +} +"); + } + + [Fact] + public void PropertyAccess_PrefixIncrementAssignment_05() + { + var src = """ +static class E +{ + extension(ref T x) where T : struct + { + public S2 P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test2(ref T f) where T : struct + { + ++f.P1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 22 (0x16) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: call ""S2 E.get_P1(ref T)"" + IL_0008: call ""S2 S2.op_Increment(S2)"" + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: call ""void E.set_P1(ref T, S2)"" + IL_0014: nop + IL_0015: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int P1 { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + ++default(T).P1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,11): error CS1510: A ref or out value must be an assignable variable + // ++default(T).P1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 11), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void PropertyAccess_PrefixIncrementAssignment_06() + { + var src = """ +static class E +{ + extension(T x) + { + public S2 P1 + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return default; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return x; + } +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test1(ref F); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new C1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test1(ref T f) + { + ++f.P1; + } + + static void Test2(ref T f) where T : class + { + ++f.P1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T V_0, + T& V_1, + S2 V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""S2 E.get_P1(T)"" + IL_002a: call ""S2 S2.op_Increment(S2)"" + IL_002f: stloc.2 + IL_0030: ldobj ""T"" + IL_0035: ldloc.2 + IL_0036: call ""void E.set_P1(T, S2)"" + IL_003b: nop + IL_003c: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 27 (0x1b) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: dup + IL_0008: call ""S2 E.get_P1(T)"" + IL_000d: call ""S2 S2.op_Increment(S2)"" + IL_0012: stloc.0 + IL_0013: ldloc.0 + IL_0014: call ""void E.set_P1(T, S2)"" + IL_0019: nop + IL_001a: ret +} +"); + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public S2 P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this.P1++; + } +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 36 (0x24) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: ldobj ""S1"" + IL_000c: call ""S2 E.get_P1(S1)"" + IL_0011: call ""S2 S2.op_Increment(S2)"" + IL_0016: stloc.0 + IL_0017: ldobj ""S1"" + IL_001c: ldloc.0 + IL_001d: call ""void E.set_P1(S1, S2)"" + IL_0022: nop + IL_0023: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""S2 E.get_P1(S1)"" + IL_000c: call ""S2 S2.op_Increment(S2)"" + IL_0011: stloc.0 + IL_0012: ldarg.0 + IL_0013: ldobj ""S1"" + IL_0018: ldloc.0 + IL_0019: call ""void E.set_P1(S1, S2)"" + IL_001e: nop + IL_001f: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_PostfixIncrementAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public S2 P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this.P1++; + } +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 26 (0x1a) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: call ""S2 E.get_P1(" + refKind + @" S1)"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: call ""S2 S2.op_Increment(S2)"" + IL_0013: call ""void E.set_P1(" + refKind + @" S1, S2)"" + IL_0018: nop + IL_0019: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 22 (0x16) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""S2 E.get_P1(" + refKind + @" S1)"" + IL_0007: stloc.0 + IL_0008: ldarg.0 + IL_0009: ldloc.0 + IL_000a: call ""S2 S2.op_Increment(S2)"" + IL_000f: call ""void E.set_P1(" + refKind + @" S1, S2)"" + IL_0014: nop + IL_0015: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int P1 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1).P1++; + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (15,9): error CS1510: A ref or out value must be an assignable variable + // default(S1).P1++; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // default(S1).P1++; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1++; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1++; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public S2 P1 + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return default; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return x; + } +} + +class Program +{ + public static C1 F = new C1 { F1 = 123 }; + + static void Main() + { + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 26 (0x1a) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: dup + IL_0007: call ""S2 E.get_P1(C1)"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: call ""S2 S2.op_Increment(S2)"" + IL_0013: call ""void E.set_P1(C1, S2)"" + IL_0018: nop + IL_0019: ret +} +"); + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_04() + { + var src = """ +static class E +{ + extension(T x) + { + public S2 P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test1(ref F); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test1(ref T f) + { + f.P1++; + } + + static void Test2(ref T f) where T : struct + { + f.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T V_0, + T& V_1, + S2 V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""S2 E.get_P1(T)"" + IL_002a: call ""S2 S2.op_Increment(S2)"" + IL_002f: stloc.2 + IL_0030: ldobj ""T"" + IL_0035: ldloc.2 + IL_0036: call ""void E.set_P1(T, S2)"" + IL_003b: nop + IL_003c: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: call ""S2 E.get_P1(T)"" + IL_000d: call ""S2 S2.op_Increment(S2)"" + IL_0012: stloc.0 + IL_0013: ldobj ""T"" + IL_0018: ldloc.0 + IL_0019: call ""void E.set_P1(T, S2)"" + IL_001e: nop + IL_001f: ret +} +"); + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_05() + { + var src = """ +static class E +{ + extension(ref T x) where T : struct + { + public S2 P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test2(ref T f) where T : struct + { + f.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 22 (0x16) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: call ""S2 E.get_P1(ref T)"" + IL_0008: stloc.0 + IL_0009: ldloc.0 + IL_000a: call ""S2 S2.op_Increment(S2)"" + IL_000f: call ""void E.set_P1(ref T, S2)"" + IL_0014: nop + IL_0015: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int P1 { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T).P1++; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS1510: A ref or out value must be an assignable variable + // default(T).P1++; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_06() + { + var src = """ +static class E +{ + extension(T x) + { + public S2 P1 + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return default; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return x; + } +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test1(ref F); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new C1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test1(ref T f) + { + f.P1++; + } + + static void Test2(ref T f) where T : class + { + f.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T V_0, + T& V_1, + S2 V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""S2 E.get_P1(T)"" + IL_002a: call ""S2 S2.op_Increment(S2)"" + IL_002f: stloc.2 + IL_0030: ldobj ""T"" + IL_0035: ldloc.2 + IL_0036: call ""void E.set_P1(T, S2)"" + IL_003b: nop + IL_003c: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 27 (0x1b) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: dup + IL_0008: call ""S2 E.get_P1(T)"" + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: call ""S2 S2.op_Increment(S2)"" + IL_0014: call ""void E.set_P1(T, S2)"" + IL_0019: nop + IL_001a: ret +} +"); + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public object P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + + public int? P2 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test1() + { + this.P1 ??= Program.Get1(); + } + + public void Test2() + { + this.P2 ??= Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + Test2(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test2(); + System.Console.Write(F.F1); + } + + static void Test1() + { + F.P1 ??= Get1(); + } + + static void Test2() + { + F.P2 ??= Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", +@" +{ + // Code size 47 (0x2f) + .maxstack 3 + .locals init (S1& V_0, + object V_1, + object V_2) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldobj ""S1"" + IL_000d: call ""object E.get_P1(S1)"" + IL_0012: brtrue.s IL_002e + IL_0014: call ""int Program.Get1()"" + IL_0019: box ""int"" + IL_001e: stloc.1 + IL_001f: ldloc.0 + IL_0020: ldobj ""S1"" + IL_0025: ldloc.1 + IL_0026: dup + IL_0027: stloc.2 + IL_0028: call ""void E.set_P1(S1, object)"" + IL_002d: nop + IL_002e: ret +} +"); + + verifier.VerifyIL("S1.Test1", +@" +{ + // Code size 41 (0x29) + .maxstack 3 + .locals init (object V_0, + object V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""object E.get_P1(S1)"" + IL_000c: brtrue.s IL_0028 + IL_000e: call ""int Program.Get1()"" + IL_0013: box ""int"" + IL_0018: stloc.0 + IL_0019: ldarg.0 + IL_001a: ldobj ""S1"" + IL_001f: ldloc.0 + IL_0020: dup + IL_0021: stloc.1 + IL_0022: call ""void E.set_P1(S1, object)"" + IL_0027: nop + IL_0028: ret +} +"); + + verifier.VerifyIL("Program.Test2", +@" +{ + // Code size 66 (0x42) + .maxstack 3 + .locals init (S1& V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldobj ""S1"" + IL_000d: call ""int? E.get_P2(S1)"" + IL_0012: stloc.1 + IL_0013: ldloca.s V_1 + IL_0015: call ""int int?.GetValueOrDefault()"" + IL_001a: stloc.2 + IL_001b: ldloca.s V_1 + IL_001d: call ""bool int?.HasValue.get"" + IL_0022: brtrue.s IL_0041 + IL_0024: call ""int Program.Get1()"" + IL_0029: stloc.2 + IL_002a: ldloc.0 + IL_002b: ldobj ""S1"" + IL_0030: ldloca.s V_3 + IL_0032: ldloc.2 + IL_0033: call ""int?..ctor(int)"" + IL_0038: ldloc.3 + IL_0039: call ""void E.set_P2(S1, int?)"" + IL_003e: nop + IL_003f: br.s IL_0041 + IL_0041: ret +} +"); + + verifier.VerifyIL("S1.Test2", +@" +{ + // Code size 60 (0x3c) + .maxstack 3 + .locals init (int? V_0, + int V_1, + int? V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""int? E.get_P2(S1)"" + IL_000c: stloc.0 + IL_000d: ldloca.s V_0 + IL_000f: call ""int int?.GetValueOrDefault()"" + IL_0014: stloc.1 + IL_0015: ldloca.s V_0 + IL_0017: call ""bool int?.HasValue.get"" + IL_001c: brtrue.s IL_003b + IL_001e: call ""int Program.Get1()"" + IL_0023: stloc.1 + IL_0024: ldarg.0 + IL_0025: ldobj ""S1"" + IL_002a: ldloca.s V_2 + IL_002c: ldloc.1 + IL_002d: call ""int?..ctor(int)"" + IL_0032: ldloc.2 + IL_0033: call ""void E.set_P2(S1, int?)"" + IL_0038: nop + IL_0039: br.s IL_003b + IL_003b: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_ConditionalAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public object P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + public int? P2 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test1() + { + this.P1 ??= Program.Get1(); + } + + public void Test2() + { + this.P2 ??= Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + Test2(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test2(); + System.Console.Write(F.F1); + } + + static void Test1() + { + F.P1 ??= Get1(); + } + + static void Test2() + { + F.P2 ??= Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (S1& V_0, + object V_1) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""object E.get_P1(" + refKind + @" S1)"" + IL_000d: brtrue.s IL_0022 + IL_000f: ldloc.0 + IL_0010: call ""int Program.Get1()"" + IL_0015: box ""int"" + IL_001a: dup + IL_001b: stloc.1 + IL_001c: call ""void E.set_P1(" + refKind + @" S1, object)"" + IL_0021: nop + IL_0022: ret +} +"); + + verifier.VerifyIL("S1.Test1", +@" +{ + // Code size 29 (0x1d) + .maxstack 3 + .locals init (object V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""object E.get_P1(" + refKind + @" S1)"" + IL_0007: brtrue.s IL_001c + IL_0009: ldarg.0 + IL_000a: call ""int Program.Get1()"" + IL_000f: box ""int"" + IL_0014: dup + IL_0015: stloc.0 + IL_0016: call ""void E.set_P1(" + refKind + @" S1, object)"" + IL_001b: nop + IL_001c: ret +} +"); + + verifier.VerifyIL("Program.Test2", +@" +{ + // Code size 56 (0x38) + .maxstack 3 + .locals init (S1& V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int? E.get_P2(" + refKind + @" S1)"" + IL_000d: stloc.1 + IL_000e: ldloca.s V_1 + IL_0010: call ""int int?.GetValueOrDefault()"" + IL_0015: stloc.2 + IL_0016: ldloca.s V_1 + IL_0018: call ""bool int?.HasValue.get"" + IL_001d: brtrue.s IL_0037 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldloc.0 + IL_0026: ldloca.s V_3 + IL_0028: ldloc.2 + IL_0029: call ""int?..ctor(int)"" + IL_002e: ldloc.3 + IL_002f: call ""void E.set_P2(" + refKind + @" S1, int?)"" + IL_0034: nop + IL_0035: br.s IL_0037 + IL_0037: ret +} +"); + + verifier.VerifyIL("S1.Test2", +@" +{ + // Code size 50 (0x32) + .maxstack 3 + .locals init (int? V_0, + int V_1, + int? V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int? E.get_P2(" + refKind + @" S1)"" + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: call ""int int?.GetValueOrDefault()"" + IL_000f: stloc.1 + IL_0010: ldloca.s V_0 + IL_0012: call ""bool int?.HasValue.get"" + IL_0017: brtrue.s IL_0031 + IL_0019: call ""int Program.Get1()"" + IL_001e: stloc.1 + IL_001f: ldarg.0 + IL_0020: ldloca.s V_2 + IL_0022: ldloc.1 + IL_0023: call ""int?..ctor(int)"" + IL_0028: ldloc.2 + IL_0029: call ""void E.set_P2(" + refKind + @" S1, int?)"" + IL_002e: nop + IL_002f: br.s IL_0031 + IL_0031: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public object P1 { get => 0; set {} } + public int? P2 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test1() + { + default(S1).P1 ??= 1; + } + static void Test2() + { + default(S1).P2 ??= 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (16,9): error CS1510: A ref or out value must be an assignable variable + // default(S1).P1 ??= 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), + // (20,9): error CS1510: A ref or out value must be an assignable variable + // default(S1).P2 ??= 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(20, 9) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // default(S1).P1 ??= 1; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), + // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 ??= 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(16, 9), + // (20,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // default(S1).P2 ??= 1; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(20, 9), + // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P2 ??= 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P2").WithLocation(20, 9) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 ??= 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(16, 9), + // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P2 ??= 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P2").WithLocation(20, 9) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public object P1 + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + public int? P2 + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new C1 { F1 = 123 }; + Test2(); + System.Console.Write(F.F1); + } + + static void Test1() + { + F.P1 ??= Get1(); + } + + static void Test2() + { + F.P2 ??= Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (C1 V_0, + object V_1) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""object E.get_P1(C1)"" + IL_000d: brtrue.s IL_0022 + IL_000f: ldloc.0 + IL_0010: call ""int Program.Get1()"" + IL_0015: box ""int"" + IL_001a: dup + IL_001b: stloc.1 + IL_001c: call ""void E.set_P1(C1, object)"" + IL_0021: nop + IL_0022: ret +} +"); + + verifier.VerifyIL("Program.Test2", +@" +{ + // Code size 56 (0x38) + .maxstack 3 + .locals init (C1 V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int? E.get_P2(C1)"" + IL_000d: stloc.1 + IL_000e: ldloca.s V_1 + IL_0010: call ""int int?.GetValueOrDefault()"" + IL_0015: stloc.2 + IL_0016: ldloca.s V_1 + IL_0018: call ""bool int?.HasValue.get"" + IL_001d: brtrue.s IL_0037 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldloc.0 + IL_0026: ldloca.s V_3 + IL_0028: ldloc.2 + IL_0029: call ""int?..ctor(int)"" + IL_002e: ldloc.3 + IL_002f: call ""void E.set_P2(C1, int?)"" + IL_0034: nop + IL_0035: br.s IL_0037 + IL_0037: ret +} +"); + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public object P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + public int? P2 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test11(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test12(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test13(); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test21(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test22(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test23(); + System.Console.Write(Program.F.F1); + } + + static void Test11(ref T f) + { + f.P1 ??= Get1(); + } + + static void Test21(ref T f) + { + f.P2 ??= Get1(); + } + + static void Test12(ref T f) where T : struct + { + f.P1 ??= Get1(); + } + + static void Test22(ref T f) where T : struct + { + f.P2 ??= Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test13() + { + Program.F.P1 ??= await Get1Async(); + } + + static async Task Test23() + { + Program.F.P2 ??= await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test11(ref T)", +@" +{ + // Code size 75 (0x4b) + .maxstack 3 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3, + object V_4, + object V_5) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""object E.get_P1(T)"" + IL_002b: brtrue.s IL_004a + IL_002d: call ""int Program.Get1()"" + IL_0032: box ""int"" + IL_0037: stloc.s V_4 + IL_0039: ldloc.0 + IL_003a: ldobj ""T"" + IL_003f: ldloc.s V_4 + IL_0041: dup + IL_0042: stloc.s V_5 + IL_0044: call ""void E.set_P1(T, object)"" + IL_0049: nop + IL_004a: ret +} +"); + + verifier.VerifyIL("Program.Test12(ref T)", +@" +{ + // Code size 43 (0x2b) + .maxstack 3 + .locals init (T& V_0, + object V_1, + object V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""T"" + IL_0009: call ""object E.get_P1(T)"" + IL_000e: brtrue.s IL_002a + IL_0010: call ""int Program.Get1()"" + IL_0015: box ""int"" + IL_001a: stloc.1 + IL_001b: ldloc.0 + IL_001c: ldobj ""T"" + IL_0021: ldloc.1 + IL_0022: dup + IL_0023: stloc.2 + IL_0024: call ""void E.set_P1(T, object)"" + IL_0029: nop + IL_002a: ret +} +"); + + verifier.VerifyIL("Program.Test21(ref T)", +@" +{ + // Code size 96 (0x60) + .maxstack 3 + .locals init (T& V_0, + T V_1, + T& V_2, + int? V_3, + int V_4, + T V_5, + int? V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: ldloc.0 + IL_0022: ldobj ""T"" + IL_0027: call ""int? E.get_P2(T)"" + IL_002c: stloc.3 + IL_002d: ldloca.s V_3 + IL_002f: call ""int int?.GetValueOrDefault()"" + IL_0034: stloc.s V_4 + IL_0036: ldloca.s V_3 + IL_0038: call ""bool int?.HasValue.get"" + IL_003d: brtrue.s IL_005f + IL_003f: call ""int Program.Get1()"" + IL_0044: stloc.s V_4 + IL_0046: ldloc.0 + IL_0047: ldobj ""T"" + IL_004c: ldloca.s V_6 + IL_004e: ldloc.s V_4 + IL_0050: call ""int?..ctor(int)"" + IL_0055: ldloc.s V_6 + IL_0057: call ""void E.set_P2(T, int?)"" + IL_005c: nop + IL_005d: br.s IL_005f + IL_005f: ret +} +"); + + verifier.VerifyIL("Program.Test22(ref T)", +@" +{ + // Code size 62 (0x3e) + .maxstack 3 + .locals init (T& V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""T"" + IL_0009: call ""int? E.get_P2(T)"" + IL_000e: stloc.1 + IL_000f: ldloca.s V_1 + IL_0011: call ""int int?.GetValueOrDefault()"" + IL_0016: stloc.2 + IL_0017: ldloca.s V_1 + IL_0019: call ""bool int?.HasValue.get"" + IL_001e: brtrue.s IL_003d + IL_0020: call ""int Program.Get1()"" + IL_0025: stloc.2 + IL_0026: ldloc.0 + IL_0027: ldobj ""T"" + IL_002c: ldloca.s V_3 + IL_002e: ldloc.2 + IL_002f: call ""int?..ctor(int)"" + IL_0034: ldloc.3 + IL_0035: call ""void E.set_P2(T, int?)"" + IL_003a: nop + IL_003b: br.s IL_003d + IL_003d: ret +} +"); + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public object P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + public int? P2 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test12(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test13(); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test22(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test23(); + System.Console.Write(Program.F.F1); + } + + static void Test12(ref T f) where T : struct + { + f.P1 ??= Get1(); + } + + static void Test22(ref T f) where T : struct + { + f.P2 ??= Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test13() where T : struct + { + Program.F.P1 ??= await Get1Async(); + } + + static async Task Test23() where T : struct + { + Program.F.P2 ??= await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test12(ref T)", +@" +{ + // Code size 31 (0x1f) + .maxstack 3 + .locals init (T& V_0, + object V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""object E.get_P1(ref T)"" + IL_0009: brtrue.s IL_001e + IL_000b: ldloc.0 + IL_000c: call ""int Program.Get1()"" + IL_0011: box ""int"" + IL_0016: dup + IL_0017: stloc.1 + IL_0018: call ""void E.set_P1(ref T, object)"" + IL_001d: nop + IL_001e: ret +} +"); + + verifier.VerifyIL("Program.Test22(ref T)", +@" +{ + // Code size 52 (0x34) + .maxstack 3 + .locals init (T& V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int? E.get_P2(ref T)"" + IL_0009: stloc.1 + IL_000a: ldloca.s V_1 + IL_000c: call ""int int?.GetValueOrDefault()"" + IL_0011: stloc.2 + IL_0012: ldloca.s V_1 + IL_0014: call ""bool int?.HasValue.get"" + IL_0019: brtrue.s IL_0033 + IL_001b: call ""int Program.Get1()"" + IL_0020: stloc.2 + IL_0021: ldloc.0 + IL_0022: ldloca.s V_3 + IL_0024: ldloc.2 + IL_0025: call ""int?..ctor(int)"" + IL_002a: ldloc.3 + IL_002b: call ""void E.set_P2(ref T, int?)"" + IL_0030: nop + IL_0031: br.s IL_0033 + IL_0033: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public object P1 { get => 0; set {} } + public int? P2 { get => 0; set {} } + } +} + +class Program +{ + static void Test1() where T : struct + { + default(T).P1 += 1; + } + static void Test2() where T : struct + { + default(T).P2 += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (14,9): error CS1510: A ref or out value must be an assignable variable + // default(T).P1 += 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), + // (18,9): error CS1510: A ref or out value must be an assignable variable + // default(T).P2 += 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(18, 9), + // (26,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(26, 25), + // (36,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(36, 35) + ); + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_06() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public object P1 + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return null; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + public int? P2 + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return null; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test11(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test12(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test13(); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test21(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test22(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test23(); + System.Console.Write(Program.F.F1); + } + + static void Test11(ref T f) + { + f.P1 ??= Get1(); + } + + static void Test21(ref T f) + { + f.P2 ??= Get1(); + } + + static void Test12(ref T f) where T : class + { + f.P1 ??= Get1(); + } + + static void Test22(ref T f) where T : class + { + f.P2 ??= Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test13() + { + Program.F.P1 ??= await Get1Async(); + } + + static async Task Test23() + { + Program.F.P2 ??= await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125:123123125:123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test11(ref T)", +@" +{ + // Code size 75 (0x4b) + .maxstack 3 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3, + object V_4, + object V_5) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""object E.get_P1(T)"" + IL_002b: brtrue.s IL_004a + IL_002d: call ""int Program.Get1()"" + IL_0032: box ""int"" + IL_0037: stloc.s V_4 + IL_0039: ldloc.0 + IL_003a: ldobj ""T"" + IL_003f: ldloc.s V_4 + IL_0041: dup + IL_0042: stloc.s V_5 + IL_0044: call ""void E.set_P1(T, object)"" + IL_0049: nop + IL_004a: ret +} +"); + + verifier.VerifyIL("Program.Test12(ref T)", +@" +{ + // Code size 36 (0x24) + .maxstack 3 + .locals init (T V_0, + object V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""object E.get_P1(T)"" + IL_000e: brtrue.s IL_0023 + IL_0010: ldloc.0 + IL_0011: call ""int Program.Get1()"" + IL_0016: box ""int"" + IL_001b: dup + IL_001c: stloc.1 + IL_001d: call ""void E.set_P1(T, object)"" + IL_0022: nop + IL_0023: ret +} +"); + + verifier.VerifyIL("Program.Test21(ref T)", +@" +{ + // Code size 96 (0x60) + .maxstack 3 + .locals init (T& V_0, + T V_1, + T& V_2, + int? V_3, + int V_4, + T V_5, + int? V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: ldloc.0 + IL_0022: ldobj ""T"" + IL_0027: call ""int? E.get_P2(T)"" + IL_002c: stloc.3 + IL_002d: ldloca.s V_3 + IL_002f: call ""int int?.GetValueOrDefault()"" + IL_0034: stloc.s V_4 + IL_0036: ldloca.s V_3 + IL_0038: call ""bool int?.HasValue.get"" + IL_003d: brtrue.s IL_005f + IL_003f: call ""int Program.Get1()"" + IL_0044: stloc.s V_4 + IL_0046: ldloc.0 + IL_0047: ldobj ""T"" + IL_004c: ldloca.s V_6 + IL_004e: ldloc.s V_4 + IL_0050: call ""int?..ctor(int)"" + IL_0055: ldloc.s V_6 + IL_0057: call ""void E.set_P2(T, int?)"" + IL_005c: nop + IL_005d: br.s IL_005f + IL_005f: ret +} +"); + + verifier.VerifyIL("Program.Test22(ref T)", +@" +{ + // Code size 57 (0x39) + .maxstack 3 + .locals init (T V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""int? E.get_P2(T)"" + IL_000e: stloc.1 + IL_000f: ldloca.s V_1 + IL_0011: call ""int int?.GetValueOrDefault()"" + IL_0016: stloc.2 + IL_0017: ldloca.s V_1 + IL_0019: call ""bool int?.HasValue.get"" + IL_001e: brtrue.s IL_0038 + IL_0020: call ""int Program.Get1()"" + IL_0025: stloc.2 + IL_0026: ldloc.0 + IL_0027: ldloca.s V_3 + IL_0029: ldloc.2 + IL_002a: call ""int?..ctor(int)"" + IL_002f: ldloc.3 + IL_0030: call ""void E.set_P2(T, int?)"" + IL_0035: nop + IL_0036: br.s IL_0038 + IL_0038: ret +} +"); + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + (this.P1, _) = (Program.Get1(), 0); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + (F.P1, _) = (Get1(), 0); + } + + public static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 25 (0x19) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldobj ""S1"" + IL_0011: ldloc.0 + IL_0012: call ""void E.set_P1(S1, int)"" + IL_0017: nop + IL_0018: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: call ""int Program.Get1()"" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(S1, int)"" + IL_0013: nop + IL_0014: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_DeconstructAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + (this.P1, _) = (Program.Get1(), 0); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + (F.P1, _) = (Get1(), 0); + } + + public static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 20 (0x14) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldloc.0 + IL_000d: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0012: nop + IL_0013: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: call ""int Program.Get1()"" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: ldloc.0 + IL_0009: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_000e: nop + IL_000f: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int P1 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + (default(S1).P1, _) = (1, 0); + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (15,10): error CS1510: A ref or out value must be an assignable variable + // (default(S1).P1, _) = (1, 0); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 10) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (15,10): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // (default(S1).P1, _) = (1, 0); + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 10), + // (15,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (default(S1).P1, _) = (1, 0); + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 10) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (15,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (default(S1).P1, _) = (1, 0); + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 10) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F = new C1 { F1 = 123 }; + + static void Main() + { + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + (F.P1, _) = (Get1(), 0); + } + + static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 20 (0x14) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldloc.0 + IL_000d: call ""void E.set_P1(C1, int)"" + IL_0012: nop + IL_0013: ret +} +"); + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + (f.P1, _) = (Get1(), 0); + } + + static void Test2(ref T f) where T : struct + { + (f.P1, _) = (Get1(), 0); + } + + static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + (Program.F.P1, _) = (await Get1Async(), 0); + } + + static async Task Get1Async() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124:123124124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.3 + IL_0025: ldobj ""T"" + IL_002a: ldloc.3 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""T"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(T, int)"" + IL_0013: nop + IL_0014: ret +} +"); + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + (f.P1, _) = (Get1(), 0); + } + + static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + (Program.F.P1, _) = (await Get1Async(), 0); + } + + static async Task Get1Async() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""void E.set_P1(ref T, int)"" + IL_000e: nop + IL_000f: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int P1 { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + (default(T).P1, _) = (1, 0); + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,10): error CS1510: A ref or out value must be an assignable variable + // (default(T).P1, _) = (1, 0); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 10), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_06() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + (f.P1, _) = (Get1(), 0); + } + + static void Test2(ref T f) where T : class + { + (f.P1, _) = (Get1(), 0); + } + + static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + (Program.F.P1, _) = (await Get1Async(), 0); + } + + static async Task Get1Async() + { + System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123124:123123124:123123124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.3 + IL_0025: ldobj ""T"" + IL_002a: ldloc.3 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(T, int)"" + IL_0013: nop + IL_0014: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get2()] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get2()] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } + + public static int Get2() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 45 (0x2d) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get2()"" + IL_000b: stloc.0 + IL_000c: dup + IL_000d: ldobj ""S1"" + IL_0012: ldloc.0 + IL_0013: call ""int E.get_Item(S1, int)"" + IL_0018: call ""int Program.Get1()"" + IL_001d: add + IL_001e: stloc.1 + IL_001f: ldobj ""S1"" + IL_0024: ldloc.0 + IL_0025: ldloc.1 + IL_0026: call ""void E.set_Item(S1, int, int)"" + IL_002b: nop + IL_002c: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 41 (0x29) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: nop + IL_0001: call ""int Program.Get2()"" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""int E.get_Item(S1, int)"" + IL_0013: call ""int Program.Get1()"" + IL_0018: add + IL_0019: stloc.1 + IL_001a: ldarg.0 + IL_001b: ldobj ""S1"" + IL_0020: ldloc.0 + IL_0021: ldloc.1 + IL_0022: call ""void E.set_Item(S1, int, int)"" + IL_0027: nop + IL_0028: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0] += 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + ); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_CompoundAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get2()] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get2()] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } + + public static int Get2() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 35 (0x23) + .maxstack 4 + .locals init (S1& V_0, + int V_1) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get2()"" + IL_000c: stloc.1 + IL_000d: ldloc.0 + IL_000e: ldloc.1 + IL_000f: ldloc.0 + IL_0010: ldloc.1 + IL_0011: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: add + IL_001c: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_0021: nop + IL_0022: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 4 + .locals init (int V_0) + IL_0000: nop + IL_0001: call ""int Program.Get2()"" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: ldloc.0 + IL_0009: ldarg.0 + IL_000a: ldloc.0 + IL_000b: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0] += 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get2()] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static int Get2() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 35 (0x23) + .maxstack 4 + .locals init (C1 V_0, + int V_1) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get2()"" + IL_000c: stloc.1 + IL_000d: ldloc.0 + IL_000e: ldloc.1 + IL_000f: ldloc.0 + IL_0010: ldloc.1 + IL_0011: call ""int E.get_Item(C1, int)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: add + IL_001c: call ""void E.set_Item(C1, int, int)"" + IL_0021: nop + IL_0022: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[0] += Get1(); + } + + static void Test2(ref T f) where T : struct + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 64 (0x40) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: ldc.i4.0 + IL_0026: call ""int E.get_Item(T, int)"" + IL_002b: call ""int Program.Get1()"" + IL_0030: add + IL_0031: stloc.3 + IL_0032: ldobj ""T"" + IL_0037: ldc.i4.0 + IL_0038: ldloc.3 + IL_0039: call ""void E.set_Item(T, int, int)"" + IL_003e: nop + IL_003f: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: ldc.i4.0 + IL_0009: call ""int E.get_Item(T, int)"" + IL_000e: call ""int Program.Get1()"" + IL_0013: add + IL_0014: stloc.0 + IL_0015: ldobj ""T"" + IL_001a: ldc.i4.0 + IL_001b: ldloc.0 + IL_001c: call ""void E.set_Item(T, int, int)"" + IL_0021: nop + IL_0022: ret +} +"); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 25 (0x19) + .maxstack 4 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: ldc.i4.0 + IL_0007: call ""int E.get_Item(ref T, int)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_Item(ref T, int, int)"" + IL_0017: nop + IL_0018: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_06() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[0] += Get1(); + } + + static void Test2(ref T f) where T : class + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 64 (0x40) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: ldc.i4.0 + IL_0026: call ""int E.get_Item(T, int)"" + IL_002b: call ""int Program.Get1()"" + IL_0030: add + IL_0031: stloc.3 + IL_0032: ldobj ""T"" + IL_0037: ldc.i4.0 + IL_0038: ldloc.3 + IL_0039: call ""void E.set_Item(T, int, int)"" + IL_003e: nop + IL_003f: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 30 (0x1e) + .maxstack 4 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.0 + IL_000a: ldloc.0 + IL_000b: ldc.i4.0 + IL_000c: call ""int E.get_Item(T, int)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: add + IL_0017: call ""void E.set_Item(T, int, int)"" + IL_001c: nop + IL_001d: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_07() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + GetT()[0] += Get1(); + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT()[0] += await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 29 (0x1d) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: call ""int E.get_Item(T, int)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldc.i4.0 + IL_0015: ldloc.0 + IL_0016: call ""void E.set_Item(T, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_08() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + GetT()[0] += Get1(); + } + + static void Test2() where T : class + { + GetT()[0] += Get1(); + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT()[0] += await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 29 (0x1d) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: call ""int E.get_Item(T, int)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldc.i4.0 + IL_0015: ldloc.0 + IL_0016: call ""void E.set_Item(T, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 29 (0x1d) + .maxstack 4 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.0 + IL_0009: ldloc.0 + IL_000a: ldc.i4.0 + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(T, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 63 (0x3f) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: ldobj ""S1"" + IL_0015: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001a: stloc.2 + IL_001b: ldloc.0 + IL_001c: ldobj ""S1"" + IL_0021: ldloc.1 + IL_0022: ldloc.2 + IL_0023: call ""int E.get_Item(S1, int, InterpolationHandler)"" + IL_0028: call ""int Program.Get1()"" + IL_002d: add + IL_002e: stloc.3 + IL_002f: ldloc.0 + IL_0030: ldobj ""S1"" + IL_0035: ldloc.1 + IL_0036: ldloc.2 + IL_0037: ldloc.3 + IL_0038: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_003d: nop + IL_003e: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 59 (0x3b) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""S1"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldobj ""S1"" + IL_001d: ldloc.1 + IL_001e: ldloc.2 + IL_001f: call ""int E.get_Item(S1, int, InterpolationHandler)"" + IL_0024: call ""int Program.Get1()"" + IL_0029: add + IL_002a: stloc.3 + IL_002b: ldloc.0 + IL_002c: ldobj ""S1"" + IL_0031: ldloc.1 + IL_0032: ldloc.2 + IL_0033: ldloc.3 + IL_0034: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_0039: nop + IL_003a: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] += 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_02(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 46 (0x2e) + .maxstack 6 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 42 (0x2a) + .maxstack 6 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: stloc.2 + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloc.0 + IL_0016: ldloc.1 + IL_0017: ldloc.2 + IL_0018: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler)"" + IL_001d: call ""int Program.Get1()"" + IL_0022: add + IL_0023: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_0028: nop + IL_0029: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] += 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 46 (0x2e) + .maxstack 6 + .locals init (C1 V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(C1, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(C1, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] += Get1(); + } + + static void Test2(ref T f) where T : struct + { + f[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + Program.F[Get1(), $""] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127:124125127127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 94 (0x5e) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + int V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: ldloc.0 + IL_0037: ldobj ""T"" + IL_003c: ldloc.3 + IL_003d: ldloc.s V_4 + IL_003f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0044: call ""int Program.Get1()"" + IL_0049: add + IL_004a: stloc.s V_6 + IL_004c: ldloc.0 + IL_004d: ldobj ""T"" + IL_0052: ldloc.3 + IL_0053: ldloc.s V_4 + IL_0055: ldloc.s V_6 + IL_0057: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_005c: nop + IL_005d: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 59 (0x3b) + .maxstack 4 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""T"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldobj ""T"" + IL_001d: ldloc.1 + IL_001e: ldloc.2 + IL_001f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0024: call ""int Program.Get1()"" + IL_0029: add + IL_002a: stloc.3 + IL_002b: ldloc.0 + IL_002c: ldobj ""T"" + IL_0031: ldloc.1 + IL_0032: ldloc.2 + IL_0033: ldloc.3 + IL_0034: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0039: nop + IL_003a: ret +} +"); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0, $""] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F[Get1(), $""] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 42 (0x2a) + .maxstack 6 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: stloc.2 + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloc.0 + IL_0016: ldloc.1 + IL_0017: ldloc.2 + IL_0018: call ""int E.get_Item(ref T, int, InterpolationHandler)"" + IL_001d: call ""int Program.Get1()"" + IL_0022: add + IL_0023: call ""void E.set_Item(ref T, int, InterpolationHandler, int)"" + IL_0028: nop + IL_0029: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0, $""] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_06() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] += Get1(); + } + + static void Test2(ref T f) where T : class + { + f[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + Program.F[Get1(), $""] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123127:123123123127:123123123127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 94 (0x5e) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + int V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: ldloc.0 + IL_0037: ldobj ""T"" + IL_003c: ldloc.3 + IL_003d: ldloc.s V_4 + IL_003f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0044: call ""int Program.Get1()"" + IL_0049: add + IL_004a: stloc.s V_6 + IL_004c: ldloc.0 + IL_004d: ldobj ""T"" + IL_0052: ldloc.3 + IL_0053: ldloc.s V_4 + IL_0055: ldloc.s V_6 + IL_0057: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_005c: nop + IL_005d: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 47 (0x2f) + .maxstack 6 + .locals init (T V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: stloc.1 + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: ldloc.2 + IL_001a: ldloc.0 + IL_001b: ldloc.1 + IL_001c: ldloc.2 + IL_001d: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0022: call ""int Program.Get1()"" + IL_0027: add + IL_0028: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002d: nop + IL_002e: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_07() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Test1(); + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); + + //await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1(), $""] += Get1(); + } + + static int Get1() + { + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[Get1(), $""] += await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 48 (0x30) + .maxstack 4 + .locals init (T V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_001e: call ""int Program.Get1()"" + IL_0023: add + IL_0024: stloc.3 + IL_0025: ldloc.0 + IL_0026: ldloc.1 + IL_0027: ldloc.2 + IL_0028: ldloc.3 + IL_0029: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002e: nop + IL_002f: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_08() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); + + //await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1(), $""] += Get1(); + } + + static void Test2() where T : class + { + GetT()[Get1(), $""] += Get1(); + } + + static int Get1() + { + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[Get1(), $""] += await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123:123123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 48 (0x30) + .maxstack 4 + .locals init (T V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_001e: call ""int Program.Get1()"" + IL_0023: add + IL_0024: stloc.3 + IL_0025: ldloc.0 + IL_0026: ldloc.1 + IL_0027: ldloc.2 + IL_0028: ldloc.3 + IL_0029: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002e: nop + IL_002f: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 46 (0x2e) + .maxstack 6 + .locals init (T V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_WithInterpolationHandler_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] = Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] = Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 49 (0x31) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: ldobj ""S1"" + IL_0015: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001a: stloc.2 + IL_001b: call ""int Program.Get1()"" + IL_0020: stloc.3 + IL_0021: ldloc.0 + IL_0022: ldobj ""S1"" + IL_0027: ldloc.1 + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_002f: nop + IL_0030: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 45 (0x2d) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""S1"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0016: stloc.2 + IL_0017: call ""int Program.Get1()"" + IL_001c: stloc.3 + IL_001d: ldloc.0 + IL_001e: ldobj ""S1"" + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_002b: nop + IL_002c: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] = 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Set_WithInterpolationHandler_02(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] = Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] = Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001b: nop + IL_001c: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] = 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_WithInterpolationHandler_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(C1, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Set_WithInterpolationHandler_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] = Get1(); + } + + static void Test2(ref T f) where T : struct + { + f[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F[Get1(), $""] = await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 79 (0x4f) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + int V_5, + T V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_6 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_6 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: call ""int Program.Get1()"" + IL_003b: stloc.s V_5 + IL_003d: ldloc.0 + IL_003e: ldobj ""T"" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.s V_5 + IL_0048: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_004d: nop + IL_004e: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 45 (0x2d) + .maxstack 4 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""T"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: call ""int Program.Get1()"" + IL_001c: stloc.3 + IL_001d: ldloc.0 + IL_001e: ldobj ""T"" + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002b: nop + IL_002c: ret +} +"); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0, $""] = 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_WithInterpolationHandler_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F[Get1(), $""] = await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""void E.set_Item(ref T, int, InterpolationHandler, int)"" + IL_001b: nop + IL_001c: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0, $""] = 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Set_WithInterpolationHandler_06() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] = Get1(); + } + + static void Test2(ref T f) where T : class + { + f[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F[Get1(), $""] = await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126:123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 79 (0x4f) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + int V_5, + T V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_6 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_6 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: call ""int Program.Get1()"" + IL_003b: stloc.s V_5 + IL_003d: ldloc.0 + IL_003e: ldobj ""T"" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.s V_5 + IL_0048: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_004d: nop + IL_004e: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 34 (0x22) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0020: nop + IL_0021: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_WithInterpolationHandler_07() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1(), $""] = Get1(); + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT()[Get1(), $""] = await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_WithInterpolationHandler_08() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1(), $""] = Get1(); + } + + static void Test2() where T : class + { + GetT()[Get1(), $""] = Get1(); + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT()[Get1(), $""] = await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + _ = this[Program.Get1(), $"", Program.Get1()]; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Program.Get1(), $"", Get1()]; + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 49 (0x31) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: ldobj ""S1"" + IL_0015: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001a: stloc.2 + IL_001b: call ""int Program.Get1()"" + IL_0020: stloc.3 + IL_0021: ldloc.0 + IL_0022: ldobj ""S1"" + IL_0027: ldloc.1 + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_002f: pop + IL_0030: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 45 (0x2d) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""S1"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0016: stloc.2 + IL_0017: call ""int Program.Get1()"" + IL_001c: stloc.3 + IL_001d: ldloc.0 + IL_001e: ldobj ""S1"" + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_002b: pop + IL_002c: ret +} +"); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_02(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + _ = this[Program.Get1(), $"", Program.Get1()]; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Program.Get1(), $"", Get1()]; + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001b: pop + IL_001c: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + _ = default(S1)[0, $"", 1]; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! + comp2.VerifyDiagnostics( + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + _ = f[Get1(), $"", Get1()]; + } + + static void Test2(ref T f) where T : struct + { + _ = f[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // _ = Program.F[Get1(), $"", await Get1Async()]; + //} + + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 79 (0x4f) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + int V_5, + T V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_6 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_6 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: call ""int Program.Get1()"" + IL_003b: stloc.s V_5 + IL_003d: ldloc.0 + IL_003e: ldobj ""T"" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.s V_5 + IL_0048: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_004d: pop + IL_004e: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 45 (0x2d) + .maxstack 4 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""T"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: call ""int Program.Get1()"" + IL_001c: stloc.3 + IL_001d: ldloc.0 + IL_001e: ldobj ""T"" + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_002b: pop + IL_002c: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + _ = f[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + _ = Program.F[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" + IL_001b: pop + IL_001c: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + _ = default(T)[0, $"", 1]; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! + comp2.VerifyDiagnostics( + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_06() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + _ = f[Get1(), $"", Get1()]; + } + + static void Test2(ref T f) where T : class + { + _ = f[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // _ = Program.F[Get1(), $"", await Get1Async()]; + //} + + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126:123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 79 (0x4f) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + int V_5, + T V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_6 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_6 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: call ""int Program.Get1()"" + IL_003b: stloc.s V_5 + IL_003d: ldloc.0 + IL_003e: ldobj ""T"" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.s V_5 + IL_0048: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_004d: pop + IL_004e: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 34 (0x22) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +public struct S1 +{ + public int F1; +} + +class Program +{ + static void Main() + { + Test(); + } + + static void Test() + { + _ = GetS1()[Program.Get1(), $"", Get1()]; + } + + static S1 GetS1() => new S1 { F1 = 123 }; + + public static int Get1() + { + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1 V_0) + IL_0000: nop + IL_0001: call ""S1 Program.GetS1()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_02(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +unsafe struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + fixed (int* f1 = &x.F1) + { + (*f1)++; + } + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static void Main() + { + Test(); + } + + static void Test() + { + _ = GetS1()[Program.Get1(), $"", Get1()]; + } + + static S1 GetS1() => new S1 { F1 = 123 }; + + public static int Get1() + { + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "123124", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 35 (0x23) + .maxstack 5 + .locals init (S1 V_0) + IL_0000: nop + IL_0001: call ""S1 Program.GetS1()"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloca.s V_0 + IL_0012: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0017: call ""int Program.Get1()"" + IL_001c: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static void Main() + { + Test(); + } + + static void Test() + { + _ = GetC1()[Get1(), $"", Get1()]; + } + + static C1 GetC1() => new C1 { F1 = 123 }; + + static int Get1() + { + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: call ""C1 Program.GetC1()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static void Test2() where T : struct + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + x = (TR)(object)new S1 { F1 = ((S1)(object)x).F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static void Test2() where T : struct + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static int Get1() + { + return 1; + } + + static async Task Test3() where T : struct + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 35 (0x23) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloca.s V_0 + IL_0012: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0017: call ""int Program.Get1()"" + IL_001c: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_06() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static void Test2() where T : class + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_ReadonlyReceiver_020(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test({{{(refKind == "ref" ? "ref" : "in")}}} F); + System.Console.Write(F.F1); + } + + static void Test({{{refKind}}} S1 x) + { + _ = x[Program.Get1(), $"", Get1()]; + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001b: pop + IL_001c: ret +} +"); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_ReadonlyReceiver_021(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = GetS1()[Program.Get1(), $"", Get1()]; + } + + static {{{(refKind == "ref" ? "ref" : "ref readonly")}}} S1 GetS1() => ref Program.F; + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: call """ + (refKind == "ref" ? "ref" : "ref readonly") + @" S1 Program.GetS1()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_ReadonlyReceiver_022(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.Increment(); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static readonly S1 F; + + static void Main() + { + Initialize(); + Test(); + System.Console.Write(F.F1); + } + + static unsafe void Initialize() + { + fixed (int* f1 = &F.F1) + { + *f1 = 123; + } + } + + public static unsafe void Increment() + { + fixed (int* f1 = &F.F1) + { + (*f1)++; + } + } + + static void Test() + { + _ = F[Program.Get1(), $"", Get1()]; + } + + public static int Get1() + { + Increment(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public int this[int i] + { + get + { + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1()] = Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1()] = Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "125125:125125", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 32 (0x20) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: call ""int Program.Get1()"" + IL_0011: stloc.1 + IL_0012: ldobj ""S1"" + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: call ""void E.set_Item(S1, int, int)"" + IL_001e: nop + IL_001f: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 28 (0x1c) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: stloc.1 + IL_000e: ldobj ""S1"" + IL_0013: ldloc.0 + IL_0014: ldloc.1 + IL_0015: call ""void E.set_Item(S1, int, int)"" + IL_001a: nop + IL_001b: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0] = 1; + } +} +"""; + + var comp2 = CreateCompilation([src2]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0] = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + ); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Set_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1()] = Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1()] = Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "125125:125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 23 (0x17) + .maxstack 3 + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_0015: nop + IL_0016: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 19 (0x13) + .maxstack 3 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_0011: nop + IL_0012: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0] = 1; + } +} +"""; + + var comp2 = CreateCompilation([src2]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0] = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get1()] = Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 23 (0x17) + .maxstack 3 + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(C1, int, int)"" + IL_0015: nop + IL_0016: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Set_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1()] = Get1(); + } + + static void Test2(ref T f) where T : struct + { + f[Get1()] = Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F[Get1()] = await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "125125:125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 58 (0x3a) + .maxstack 3 + .locals init (T V_0, + T& V_1, + int V_2, + int V_3, + T V_4) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_4 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_4 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.1 + IL_0015: ldobj ""T"" + IL_001a: stloc.0 + IL_001b: ldloca.s V_0 + IL_001d: br.s IL_0020 + IL_001f: ldloc.1 + IL_0020: call ""int Program.Get1()"" + IL_0025: stloc.2 + IL_0026: call ""int Program.Get1()"" + IL_002b: stloc.3 + IL_002c: ldobj ""T"" + IL_0031: ldloc.2 + IL_0032: ldloc.3 + IL_0033: call ""void E.set_Item(T, int, int)"" + IL_0038: nop + IL_0039: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 28 (0x1c) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: stloc.1 + IL_000e: ldobj ""T"" + IL_0013: ldloc.0 + IL_0014: ldloc.1 + IL_0015: call ""void E.set_Item(T, int, int)"" + IL_001a: nop + IL_001b: ret +} +"); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0] = 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation([src2]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0] = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f[Get1()] = Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F[Get1()] = await Get1Async(); + } + + static async Task Get1Async() { Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "125125:125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 19 (0x13) + .maxstack 3 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_Item(ref T, int, int)"" + IL_0011: nop + IL_0012: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0] = 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation([src2]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0] = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Set_06() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1()] = Get1(); + } + + static void Test2(ref T f) where T : class + { + f[Get1()] = Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F[Get1()] = await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125:123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 58 (0x3a) + .maxstack 3 + .locals init (T V_0, + T& V_1, + int V_2, + int V_3, + T V_4) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_4 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_4 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.1 + IL_0015: ldobj ""T"" + IL_001a: stloc.0 + IL_001b: ldloca.s V_0 + IL_001d: br.s IL_0020 + IL_001f: ldloc.1 + IL_0020: call ""int Program.Get1()"" + IL_0025: stloc.2 + IL_0026: call ""int Program.Get1()"" + IL_002b: stloc.3 + IL_002c: ldobj ""T"" + IL_0031: ldloc.2 + IL_0032: ldloc.3 + IL_0033: call ""void E.set_Item(T, int, int)"" + IL_0038: nop + IL_0039: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 24 (0x18) + .maxstack 3 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int Program.Get1()"" + IL_0011: call ""void E.set_Item(T, int, int)"" + IL_0016: nop + IL_0017: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_07() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1()] = Get1(); + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT()[Get1()] = await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 23 (0x17) + .maxstack 3 + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(T, int, int)"" + IL_0015: nop + IL_0016: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Set_08() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1()] = Get1(); + } + + static void Test2() where T : class + { + GetT()[Get1()] = Get1(); + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT()[Get1()] = await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123:123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 23 (0x17) + .maxstack 3 + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(T, int, int)"" + IL_0015: nop + IL_0016: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 23 (0x17) + .maxstack 3 + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(T, int, int)"" + IL_0015: nop + IL_0016: ret +} +"); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_LValueReceiver_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + _ = this[Program.Get1()]; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Program.Get1()]; + } + + public static int Get1() + { + Program.F.F1++; return 1; } +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 25 (0x19) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldobj ""S1"" + IL_0011: ldloc.0 + IL_0012: call ""int E.get_Item(S1, int)"" + IL_0017: pop + IL_0018: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""int E.get_Item(S1, int)"" + IL_0013: pop + IL_0014: ret +} +"); + } - static async Task Test13() + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_LValueReceiver_02(string refKind) { - Program.F.P1 ??= await Get1Async(); + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } } +} - static async Task Test23() +struct S1 +{ + public int F1; + + public void Test() { - Program.F.P2 ??= await Get1Async(); + _ = this[Program.Get1()]; } +} - static async Task Get1Async() +class Program +{ + public static S1 F; + + static void Main() { - Program.F.F1++; - await Task.Yield(); + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Program.Get1()]; + } + + public static int Get1() + { + Program.F.F1++; return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125:123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test11(ref T)", + verifier.VerifyIL("Program.Test", @" { - // Code size 75 (0x4b) - .maxstack 3 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3, - object V_4, - object V_5) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""object E.get_P1(T)"" - IL_002b: brtrue.s IL_004a - IL_002d: call ""int Program.Get1()"" - IL_0032: box ""int"" - IL_0037: stloc.s V_4 - IL_0039: ldloc.0 - IL_003a: ldobj ""T"" - IL_003f: ldloc.s V_4 - IL_0041: dup - IL_0042: stloc.s V_5 - IL_0044: call ""void E.set_P1(T, object)"" - IL_0049: nop - IL_004a: ret + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0010: pop + IL_0011: ret } "); - verifier.VerifyIL("Program.Test12(ref T)", + verifier.VerifyIL("S1.Test", @" { - // Code size 43 (0x2b) - .maxstack 3 - .locals init (T& V_0, - object V_1, - object V_2) + // Code size 14 (0xe) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""T"" - IL_0009: call ""object E.get_P1(T)"" - IL_000e: brtrue.s IL_002a - IL_0010: call ""int Program.Get1()"" - IL_0015: box ""int"" - IL_001a: stloc.1 - IL_001b: ldloc.0 - IL_001c: ldobj ""T"" - IL_0021: ldloc.1 - IL_0022: dup - IL_0023: stloc.2 - IL_0024: call ""void E.set_P1(T, object)"" - IL_0029: nop - IL_002a: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_000c: pop + IL_000d: ret } "); - verifier.VerifyIL("Program.Test21(ref T)", -@" + var src2 = $$$""" +static class E { - // Code size 96 (0x60) - .maxstack 3 - .locals init (T& V_0, - T V_1, - T& V_2, - int? V_3, - int V_4, - T V_5, - int? V_6) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_5 - IL_0005: initobj ""T"" - IL_000b: ldloc.s V_5 - IL_000d: box ""T"" - IL_0012: brtrue.s IL_001f - IL_0014: ldloc.2 - IL_0015: ldobj ""T"" - IL_001a: stloc.1 - IL_001b: ldloca.s V_1 - IL_001d: br.s IL_0020 - IL_001f: ldloc.2 - IL_0020: stloc.0 - IL_0021: ldloc.0 - IL_0022: ldobj ""T"" - IL_0027: call ""int? E.get_P2(T)"" - IL_002c: stloc.3 - IL_002d: ldloca.s V_3 - IL_002f: call ""int int?.GetValueOrDefault()"" - IL_0034: stloc.s V_4 - IL_0036: ldloca.s V_3 - IL_0038: call ""bool int?.HasValue.get"" - IL_003d: brtrue.s IL_005f - IL_003f: call ""int Program.Get1()"" - IL_0044: stloc.s V_4 - IL_0046: ldloc.0 - IL_0047: ldobj ""T"" - IL_004c: ldloca.s V_6 - IL_004e: ldloc.s V_4 - IL_0050: call ""int?..ctor(int)"" - IL_0055: ldloc.s V_6 - IL_0057: call ""void E.set_P2(T, int?)"" - IL_005c: nop - IL_005d: br.s IL_005f - IL_005f: ret + extension({{{refKind}}} S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + _ = default(S1)[0]; + } +} +"""; + + var comp2 = CreateCompilation([src2]); + // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! + comp2.VerifyDiagnostics( + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_LValueReceiver_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Get1()]; + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } } -"); +"""; - verifier.VerifyIL("Program.Test22(ref T)", + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", @" { - // Code size 62 (0x3e) - .maxstack 3 - .locals init (T& V_0, - int? V_1, - int V_2, - int? V_3) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""T"" - IL_0009: call ""int? E.get_P2(T)"" - IL_000e: stloc.1 - IL_000f: ldloca.s V_1 - IL_0011: call ""int int?.GetValueOrDefault()"" - IL_0016: stloc.2 - IL_0017: ldloca.s V_1 - IL_0019: call ""bool int?.HasValue.get"" - IL_001e: brtrue.s IL_003d - IL_0020: call ""int Program.Get1()"" - IL_0025: stloc.2 - IL_0026: ldloc.0 - IL_0027: ldobj ""T"" - IL_002c: ldloca.s V_3 - IL_002e: ldloc.2 - IL_002f: call ""int?..ctor(int)"" - IL_0034: ldloc.3 - IL_0035: call ""void E.set_P2(T, int?)"" - IL_003a: nop - IL_003b: br.s IL_003d - IL_003d: ret + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(C1, int)"" + IL_0010: pop + IL_0011: ret } "); } - [Fact] - public void PropertyAccess_ConditionalAssignment_05() + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Get_LValueReceiver_04() { var src = """ using System.Threading.Tasks; static class E { - extension(ref T x) where T : struct + extension(T x) { - public object P1 - { - get - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(((S1)(object)x).F1); - } - } - public int? P2 + public int this[int i] { get { System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(((S1)(object)x).F1); + return 0; } } } @@ -8837,39 +18848,36 @@ class Program class Program { +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; - Test12(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - await Test13(); + Test1(ref Program.F); System.Console.Write(Program.F.F1); System.Console.Write(":"); Program.F = new S1 { F1 = 123 }; - Test22(ref Program.F); + Test2(ref Program.F); System.Console.Write(Program.F.F1); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - await Test23(); - System.Console.Write(Program.F.F1); + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); } - static void Test12(ref T f) where T : struct + static void Test1(ref T f) { - f.P1 ??= Get1(); + _ = f[Get1()]; } - static void Test22(ref T f) where T : struct + static void Test2(ref T f) where T : struct { - f.P2 ??= Get1(); + _ = f[Get1()]; } static int Get1() @@ -8878,187 +18886,99 @@ static int Get1() return 1; } - static async Task Test13() where T : struct - { - Program.F.P1 ??= await Get1Async(); - } - - static async Task Test23() where T : struct - { - Program.F.P2 ??= await Get1Async(); - } + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // _ = Program.F[await Get1Async()]; + //} - static async Task Get1Async() - { - Program.F.F1++; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test12(ref T)", + verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 31 (0x1f) - .maxstack 3 - .locals init (T& V_0, - object V_1) + // Code size 50 (0x32) + .maxstack 2 + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: call ""object E.get_P1(ref T)"" - IL_0009: brtrue.s IL_001e - IL_000b: ldloc.0 - IL_000c: call ""int Program.Get1()"" - IL_0011: box ""int"" - IL_0016: dup - IL_0017: stloc.1 - IL_0018: call ""void E.set_P1(ref T, object)"" - IL_001d: nop - IL_001e: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""int E.get_Item(T, int)"" + IL_0030: pop + IL_0031: ret } "); - verifier.VerifyIL("Program.Test22(ref T)", + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 52 (0x34) - .maxstack 3 - .locals init (T& V_0, - int? V_1, - int V_2, - int? V_3) + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: call ""int? E.get_P2(ref T)"" - IL_0009: stloc.1 - IL_000a: ldloca.s V_1 - IL_000c: call ""int int?.GetValueOrDefault()"" - IL_0011: stloc.2 - IL_0012: ldloca.s V_1 - IL_0014: call ""bool int?.HasValue.get"" - IL_0019: brtrue.s IL_0033 - IL_001b: call ""int Program.Get1()"" - IL_0020: stloc.2 - IL_0021: ldloc.0 - IL_0022: ldloca.s V_3 - IL_0024: ldloc.2 - IL_0025: call ""int?..ctor(int)"" - IL_002a: ldloc.3 - IL_002b: call ""void E.set_P2(ref T, int?)"" - IL_0030: nop - IL_0031: br.s IL_0033 - IL_0033: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""T"" + IL_000d: ldloc.0 + IL_000e: call ""int E.get_Item(T, int)"" + IL_0013: pop + IL_0014: ret } "); - - var src2 = """ -static class E -{ - extension(ref T x) where T : struct - { - public object P1 { get => 0; set {} } - public int? P2 { get => 0; set {} } - } -} - -class Program -{ - static void Test1() where T : struct - { - default(T).P1 += 1; - } - static void Test2() where T : struct - { - default(T).P2 += 1; - } -} - -namespace NS1 -{ - static class E - { - extension(in T x) where T : struct - { - } - } -} - -namespace NS2 -{ - static class E - { - extension(ref readonly T x) where T : struct - { - } - } -} -"""; - - var comp2 = CreateCompilation(src2); - comp2.VerifyDiagnostics( - // (14,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P1 += 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), - // (18,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P2 += 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(18, 9), - // (26,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(26, 25), - // (36,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(36, 35) - ); } - [Fact] - public void PropertyAccess_ConditionalAssignment_06() + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_LValueReceiver_05() { var src = """ using System.Threading.Tasks; static class E -{ - extension(T x) - { - public object P1 - { - get - { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return null; - } - set - { - System.Console.Write(((C1)(object)x).F1); - } - } - public int? P2 +{ + extension(ref T x) where T : struct + { + public int this[int i] { get { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return null; - } - set - { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(((S1)(object)x).F1); + return 0; } } } } -class C1 +struct S1 { public int F1; } @@ -9072,522 +18992,399 @@ class Program { static async Task Main() { - Program.F = new C1 { F1 = 123 }; - Test11(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - Test12(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - await Test13(); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - Test21(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - Test22(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - await Test23(); - System.Console.Write(Program.F.F1); - } - - static void Test11(ref T f) - { - f.P1 ??= Get1(); - } - - static void Test21(ref T f) - { - f.P2 ??= Get1(); - } - - static void Test12(ref T f) where T : class - { - f.P1 ??= Get1(); + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } - static void Test22(ref T f) where T : class + static void Test2(ref T f) where T : struct { - f.P2 ??= Get1(); + _ = f[Get1()]; } static int Get1() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + Program.F.F1++; return 1; } - static async Task Test13() - { - Program.F.P1 ??= await Get1Async(); - } - - static async Task Test23() + static async Task Test3() where T : struct { - Program.F.P2 ??= await Get1Async(); + _ = Program.F[await Get1Async()]; } static async Task Get1Async() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + Program.F.F1++; await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125:123123125:123123125:123123125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test11(ref T)", + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 75 (0x4b) - .maxstack 3 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3, - object V_4, - object V_5) + // Code size 14 (0xe) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""object E.get_P1(T)"" - IL_002b: brtrue.s IL_004a - IL_002d: call ""int Program.Get1()"" - IL_0032: box ""int"" - IL_0037: stloc.s V_4 - IL_0039: ldloc.0 - IL_003a: ldobj ""T"" - IL_003f: ldloc.s V_4 - IL_0041: dup - IL_0042: stloc.s V_5 - IL_0044: call ""void E.set_P1(T, object)"" - IL_0049: nop - IL_004a: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""int E.get_Item(ref T, int)"" + IL_000c: pop + IL_000d: ret } "); - verifier.VerifyIL("Program.Test12(ref T)", -@" + var src2 = """ +static class E { - // Code size 36 (0x24) - .maxstack 3 - .locals init (T V_0, - object V_1) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: call ""object E.get_P1(T)"" - IL_000e: brtrue.s IL_0023 - IL_0010: ldloc.0 - IL_0011: call ""int Program.Get1()"" - IL_0016: box ""int"" - IL_001b: dup - IL_001c: stloc.1 - IL_001d: call ""void E.set_P1(T, object)"" - IL_0022: nop - IL_0023: ret + extension(ref T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } } -"); - verifier.VerifyIL("Program.Test21(ref T)", -@" +class Program { - // Code size 96 (0x60) - .maxstack 3 - .locals init (T& V_0, - T V_1, - T& V_2, - int? V_3, - int V_4, - T V_5, - int? V_6) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_5 - IL_0005: initobj ""T"" - IL_000b: ldloc.s V_5 - IL_000d: box ""T"" - IL_0012: brtrue.s IL_001f - IL_0014: ldloc.2 - IL_0015: ldobj ""T"" - IL_001a: stloc.1 - IL_001b: ldloca.s V_1 - IL_001d: br.s IL_0020 - IL_001f: ldloc.2 - IL_0020: stloc.0 - IL_0021: ldloc.0 - IL_0022: ldobj ""T"" - IL_0027: call ""int? E.get_P2(T)"" - IL_002c: stloc.3 - IL_002d: ldloca.s V_3 - IL_002f: call ""int int?.GetValueOrDefault()"" - IL_0034: stloc.s V_4 - IL_0036: ldloca.s V_3 - IL_0038: call ""bool int?.HasValue.get"" - IL_003d: brtrue.s IL_005f - IL_003f: call ""int Program.Get1()"" - IL_0044: stloc.s V_4 - IL_0046: ldloc.0 - IL_0047: ldobj ""T"" - IL_004c: ldloca.s V_6 - IL_004e: ldloc.s V_4 - IL_0050: call ""int?..ctor(int)"" - IL_0055: ldloc.s V_6 - IL_0057: call ""void E.set_P2(T, int?)"" - IL_005c: nop - IL_005d: br.s IL_005f - IL_005f: ret + static void Test() where T : struct + { + _ = default(T)[0]; + } } -"); - verifier.VerifyIL("Program.Test22(ref T)", -@" +namespace NS1 { - // Code size 57 (0x39) - .maxstack 3 - .locals init (T V_0, - int? V_1, - int V_2, - int? V_3) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: call ""int? E.get_P2(T)"" - IL_000e: stloc.1 - IL_000f: ldloca.s V_1 - IL_0011: call ""int int?.GetValueOrDefault()"" - IL_0016: stloc.2 - IL_0017: ldloca.s V_1 - IL_0019: call ""bool int?.HasValue.get"" - IL_001e: brtrue.s IL_0038 - IL_0020: call ""int Program.Get1()"" - IL_0025: stloc.2 - IL_0026: ldloc.0 - IL_0027: ldloca.s V_3 - IL_0029: ldloc.2 - IL_002a: call ""int?..ctor(int)"" - IL_002f: ldloc.3 - IL_0030: call ""void E.set_P2(T, int?)"" - IL_0035: nop - IL_0036: br.s IL_0038 - IL_0038: ret + static class E + { + extension(in T x) where T : struct + { + } + } } -"); + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation([src2]); + + // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! + comp2.VerifyDiagnostics( + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); } - [Fact] - public void PropertyAccess_DeconstructAssignment_01() + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Get_LValueReceiver_06() { var src = """ +using System.Threading.Tasks; + static class E { - extension(S1 x) + extension(T x) { - public int P1 + public int this[int i] { get { - throw null; - } - set - { - System.Console.Write(x.F1); + System.Console.Write(((C1)(object)x).F1); + return 0; } } } } -struct S1 +class C1 { public int F1; +} - public void Test() - { - (this.P1, _) = (Program.Get1(), 0); - } +class Program +{ + public static T F; } class Program { - public static S1 F; - - static void Main() +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); } - static void Test() + static void Test1(ref T f) { - (F.P1, _) = (Get1(), 0); + _ = f[Get1()]; } - public static int Get1() + static void Test2(ref T f) where T : class { - System.Console.Write(Program.F.F1); - Program.F.F1++; + _ = f[Get1()]; + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // _ = Program.F[await Get1Async()]; + //} + + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", + verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 25 (0x19) + // Code size 50 (0x32) .maxstack 2 - .locals init (int V_0) + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: call ""int Program.Get1()"" - IL_000b: stloc.0 - IL_000c: ldobj ""S1"" - IL_0011: ldloc.0 - IL_0012: call ""void E.set_P1(S1, int)"" - IL_0017: nop - IL_0018: ret + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""int E.get_Item(T, int)"" + IL_0030: pop + IL_0031: ret } "); - verifier.VerifyIL("S1.Test", + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 21 (0x15) + // Code size 19 (0x13) .maxstack 2 - .locals init (int V_0) IL_0000: nop - IL_0001: call ""int Program.Get1()"" - IL_0006: stloc.0 - IL_0007: ldarg.0 - IL_0008: ldobj ""S1"" - IL_000d: ldloc.0 - IL_000e: call ""void E.set_P1(S1, int)"" - IL_0013: nop - IL_0014: ret + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int E.get_Item(T, int)"" + IL_0011: pop + IL_0012: ret } "); } - [Theory] - [InlineData("ref")] - [InlineData("ref readonly")] - [InlineData("in")] - public void PropertyAccess_DeconstructAssignment_02(string refKind) + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_RValueReceiver_01() { - var src = $$$""" + var src = """ static class E { - extension({{{refKind}}} S1 x) + extension(S1 x) { - public int P1 + public int this[int i] { get - { - throw null; - } - set { System.Console.Write(x.F1); + return 0; } } } } -struct S1 +public struct S1 { public int F1; - - public void Test() - { - (this.P1, _) = (Program.Get1(), 0); - } } class Program { - public static S1 F; - static void Main() { - F = new S1 { F1 = 123 }; Test(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); } static void Test() { - (F.P1, _) = (Get1(), 0); + _ = GetS1()[Get1()]; } + static S1 GetS1() => new S1 { F1 = 123 }; + public static int Get1() { - System.Console.Write(Program.F.F1); - Program.F.F1++; return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 20 (0x14) + // Code size 18 (0x12) .maxstack 2 - .locals init (int V_0) IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" + IL_0001: call ""S1 Program.GetS1()"" IL_0006: call ""int Program.Get1()"" - IL_000b: stloc.0 - IL_000c: ldloc.0 - IL_000d: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_0012: nop - IL_0013: ret -} -"); - - verifier.VerifyIL("S1.Test", -@" -{ - // Code size 16 (0x10) - .maxstack 2 - .locals init (int V_0) - IL_0000: nop - IL_0001: call ""int Program.Get1()"" - IL_0006: stloc.0 - IL_0007: ldarg.0 - IL_0008: ldloc.0 - IL_0009: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_000e: nop - IL_000f: ret + IL_000b: call ""int E.get_Item(S1, int)"" + IL_0010: pop + IL_0011: ret } "); + } - var src2 = $$$""" + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_RValueReceiver_02(string refKind) + { + var src = $$$""" static class E { extension({{{refKind}}} S1 x) { - public int P1 { get => 0; set {} } + public int this[int i] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } } } -struct S1; +struct S1 +{ + public int F1; +} class Program { + static void Main() + { + Test(); + } + static void Test() { - (default(S1).P1, _) = (1, 0); + _ = GetS1()[Get1()]; + } + + static S1 GetS1() => new S1 { F1 = 123 }; + + public static int Get1() + { + return 1; } } """; - var comp2 = CreateCompilation(src2); - switch (refKind) - { - case "ref": - comp2.VerifyDiagnostics( - // (15,10): error CS1510: A ref or out value must be an assignable variable - // (default(S1).P1, _) = (1, 0); - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 10) - ); - break; - case "ref readonly": - comp2.VerifyDiagnostics( - // (15,10): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // (default(S1).P1, _) = (1, 0); - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 10), - // (15,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // (default(S1).P1, _) = (1, 0); - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 10) - ); - break; - case "in": - comp2.VerifyDiagnostics( - // (15,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // (default(S1).P1, _) = (1, 0); - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 10) - ); - break; - default: - throw ExceptionUtilities.UnexpectedValue(refKind); - } + var comp = CreateCompilation([src], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "123", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (S1 V_0) + IL_0000: nop + IL_0001: call ""S1 Program.GetS1()"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" + IL_000e: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0013: pop + IL_0014: ret +} +"); } - [Fact] - public void PropertyAccess_DeconstructAssignment_03() + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_RValueReceiver_03() { var src = """ static class E { extension(C1 x) { - public int P1 + public int this[int i] { get - { - throw null; - } - set { System.Console.Write(x.F1); + return 0; } } } @@ -9600,51 +19397,46 @@ class C1 class Program { - public static C1 F = new C1 { F1 = 123 }; - static void Main() { Test(); - System.Console.Write(F.F1); } static void Test() { - (F.P1, _) = (Get1(), 0); + _ = GetC1()[Get1()]; } + static C1 GetC1() => new C1 { F1 = 123 }; + static int Get1() { - System.Console.Write(Program.F.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123124").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 20 (0x14) + // Code size 18 (0x12) .maxstack 2 - .locals init (int V_0) IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" + IL_0001: call ""C1 Program.GetC1()"" IL_0006: call ""int Program.Get1()"" - IL_000b: stloc.0 - IL_000c: ldloc.0 - IL_000d: call ""void E.set_P1(C1, int)"" - IL_0012: nop - IL_0013: ret + IL_000b: call ""int E.get_Item(C1, int)"" + IL_0010: pop + IL_0011: ret } "); } - [Fact] - public void PropertyAccess_DeconstructAssignment_04() + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_RValueReceiver_04() { var src = """ using System.Threading.Tasks; @@ -9653,15 +19445,12 @@ static class E { extension(T x) { - public int P1 + public int this[int i] { get - { - throw null; - } - set { System.Console.Write(((S1)(object)x).F1); + return 0; } } } @@ -9672,121 +19461,86 @@ struct S1 public int F1; } -class Program -{ - public static T F; -} - class Program { static async Task Main() { - Program.F = new S1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); + Test1(); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Test2(); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; await Test3(); - System.Console.Write(Program.F.F1); } - static void Test1(ref T f) + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() { - (f.P1, _) = (Get1(), 0); + _ = GetT()[Get1()]; } - static void Test2(ref T f) where T : struct + static void Test2() where T : struct { - (f.P1, _) = (Get1(), 0); + _ = GetT()[Get1()]; } static int Get1() { - System.Console.Write(Program.F.F1); - Program.F.F1++; return 1; } static async Task Test3() { - (Program.F.P1, _) = (await Get1Async(), 0); + _ = GetT()[await Get1Async()]; } static async Task Get1Async() { - System.Console.Write(Program.F.F1); - Program.F.F1++; await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124:123124124").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123:123").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 50 (0x32) + // Code size 18 (0x12) .maxstack 2 - .locals init (T V_0, - T& V_1, - T V_2, - int V_3) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_2 - IL_0005: initobj ""T"" - IL_000b: ldloc.2 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: call ""int Program.Get1()"" - IL_0024: stloc.3 - IL_0025: ldobj ""T"" - IL_002a: ldloc.3 - IL_002b: call ""void E.set_P1(T, int)"" - IL_0030: nop - IL_0031: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: pop + IL_0011: ret } "); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test2()", @" { - // Code size 21 (0x15) + // Code size 18 (0x12) .maxstack 2 - .locals init (int V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""int Program.Get1()"" - IL_0007: stloc.0 - IL_0008: ldobj ""T"" - IL_000d: ldloc.0 - IL_000e: call ""void E.set_P1(T, int)"" - IL_0013: nop - IL_0014: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: pop + IL_0011: ret } "); } - [Fact] - public void PropertyAccess_DeconstructAssignment_05() + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_RValueReceiver_05() { var src = """ using System.Threading.Tasks; @@ -9795,15 +19549,12 @@ static class E { extension(ref T x) where T : struct { - public int P1 + public int this[int i] { get - { - throw null; - } - set { System.Console.Write(((S1)(object)x).F1); + return 0; } } } @@ -9814,127 +19565,66 @@ struct S1 public int F1; } -class Program -{ - public static T F; -} - class Program { static async Task Main() { - Program.F = new S1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Test2(); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; await Test3(); - System.Console.Write(Program.F.F1); } - static void Test2(ref T f) where T : struct + static void Test2() where T : struct { - (f.P1, _) = (Get1(), 0); + _ = GetT()[Get1()]; } + static T GetT() => (T)(object)new S1 { F1 = 123 }; + static int Get1() { - System.Console.Write(Program.F.F1); - Program.F.F1++; return 1; } static async Task Test3() where T : struct { - (Program.F.P1, _) = (await Get1Async(), 0); + _ = GetT()[await Get1Async()]; } static async Task Get1Async() { - System.Console.Write(Program.F.F1); - Program.F.F1++; await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test2()", @" { - // Code size 16 (0x10) + // Code size 21 (0x15) .maxstack 2 - .locals init (int V_0) + .locals init (T V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""int Program.Get1()"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: call ""void E.set_P1(ref T, int)"" - IL_000e: nop - IL_000f: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" + IL_000e: call ""int E.get_Item(ref T, int)"" + IL_0013: pop + IL_0014: ret } "); - - var src2 = """ -static class E -{ - extension(ref T x) where T : struct - { - public int P1 { get => 0; set {} } - } -} - -class Program -{ - static void Test() where T : struct - { - (default(T).P1, _) = (1, 0); - } -} - -namespace NS1 -{ - static class E - { - extension(in T x) where T : struct - { - } - } -} - -namespace NS2 -{ - static class E - { - extension(ref readonly T x) where T : struct - { - } - } -} -"""; - - var comp2 = CreateCompilation(src2); - comp2.VerifyDiagnostics( - // (13,10): error CS1510: A ref or out value must be an assignable variable - // (default(T).P1, _) = (1, 0); - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 10), - // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), - // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) - ); } - [Fact] - public void PropertyAccess_DeconstructAssignment_06() + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] + public void IndexerAccess_Get_RValueReceiver_06() { var src = """ using System.Threading.Tasks; @@ -9943,15 +19633,12 @@ static class E { extension(T x) { - public int P1 + public int this[int i] { get - { - throw null; - } - set { System.Console.Write(((C1)(object)x).F1); + return 0; } } } @@ -9962,115 +19649,79 @@ class C1 public int F1; } -class Program -{ - public static T F; -} - class Program { static async Task Main() { - Program.F = new C1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); + Test1(); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Test2(); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; await Test3(); - System.Console.Write(Program.F.F1); } - static void Test1(ref T f) + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() { - (f.P1, _) = (Get1(), 0); + _ = GetT()[Get1()]; } - static void Test2(ref T f) where T : class + static void Test2() where T : class { - (f.P1, _) = (Get1(), 0); + _ = GetT()[Get1()]; } static int Get1() { - System.Console.Write(Program.F.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static async Task Test3() { - (Program.F.P1, _) = (await Get1Async(), 0); + _ = GetT()[await Get1Async()]; } static async Task Get1Async() { - System.Console.Write(Program.F.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123124:123123124:123123124").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123:123").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 50 (0x32) + // Code size 18 (0x12) .maxstack 2 - .locals init (T V_0, - T& V_1, - T V_2, - int V_3) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_2 - IL_0005: initobj ""T"" - IL_000b: ldloc.2 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: call ""int Program.Get1()"" - IL_0024: stloc.3 - IL_0025: ldobj ""T"" - IL_002a: ldloc.3 - IL_002b: call ""void E.set_P1(T, int)"" - IL_0030: nop - IL_0031: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: pop + IL_0011: ret } "); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test2()", @" { - // Code size 21 (0x15) + // Code size 18 (0x12) .maxstack 2 - .locals init (int V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: stloc.0 - IL_000d: ldloc.0 - IL_000e: call ""void E.set_P1(T, int)"" - IL_0013: nop - IL_0014: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: pop + IL_0011: ret } "); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 7ae0f73ffafb3..a84140d4ecea2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -11687,6 +11687,182 @@ Creating DummyHandler verifier.VerifyDiagnostics(); } + [Fact] + public void StructReceiver_Lvalue_08() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +using System.Text; + +var l = new StructLogger(); +Console.WriteLine(""logged = {0}"", l._logged); +test(ref l); +Console.WriteLine(""logged = {0}"", l._logged); + +void test(ref readonly StructLogger l) +{ + l.Log($""log:{0}""); +} + +internal struct StructLogger +{ + public int _logged; + + public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) + { + _logged++; + Console.WriteLine($""StructLogger: "" + handler.GetContent()); + } +} + +[InterpolatedStringHandler] +internal ref struct DummyHandler +{ + private readonly StringBuilder _builder; + public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) + { + Console.WriteLine($""Creating DummyHandler""); + _builder = new StringBuilder(); + } + public string GetContent() => _builder.ToString(); + + public void AppendLiteral(string s) => _builder.Append(s); + public void AppendFormatted(T t) => _builder.Append(t); +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: @" +logged = 0 +Creating DummyHandler +StructLogger: log:0 +logged = 0 +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.<
$>g__test|0_0", +@" +{ + // Code size 53 (0x35) + .maxstack 5 + .locals init (StructLogger& V_0, + StructLogger V_1, + DummyHandler V_2) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldobj ""StructLogger"" + IL_0008: stloc.1 + IL_0009: ldloca.s V_1 + IL_000b: ldloca.s V_2 + IL_000d: ldc.i4.4 + IL_000e: ldc.i4.1 + IL_000f: ldloc.0 + IL_0010: ldobj ""StructLogger"" + IL_0015: call ""DummyHandler..ctor(int, int, StructLogger)"" + IL_001a: ldloca.s V_2 + IL_001c: ldstr ""log:"" + IL_0021: call ""void DummyHandler.AppendLiteral(string)"" + IL_0026: ldloca.s V_2 + IL_0028: ldc.i4.0 + IL_0029: call ""void DummyHandler.AppendFormatted(int)"" + IL_002e: ldloc.2 + IL_002f: call ""void StructLogger.Log(DummyHandler)"" + IL_0034: ret +} +"); + } + + [Fact] + public void StructReceiver_Lvalue_09() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +using System.Text; + +var l = new StructLogger(); +Console.WriteLine(""logged = {0}"", l._logged); +test(in l); +Console.WriteLine(""logged = {0}"", l._logged); + +void test(in StructLogger l) +{ + l.Log($""log:{0}""); +} + +internal struct StructLogger +{ + public int _logged; + + public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) + { + _logged++; + Console.WriteLine($""StructLogger: "" + handler.GetContent()); + } +} + +[InterpolatedStringHandler] +internal ref struct DummyHandler +{ + private readonly StringBuilder _builder; + public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) + { + Console.WriteLine($""Creating DummyHandler""); + _builder = new StringBuilder(); + } + public string GetContent() => _builder.ToString(); + + public void AppendLiteral(string s) => _builder.Append(s); + public void AppendFormatted(T t) => _builder.Append(t); +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: @" +logged = 0 +Creating DummyHandler +StructLogger: log:0 +logged = 0 +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.<
$>g__test|0_0", +@" +{ + // Code size 53 (0x35) + .maxstack 5 + .locals init (StructLogger& V_0, + StructLogger V_1, + DummyHandler V_2) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldobj ""StructLogger"" + IL_0008: stloc.1 + IL_0009: ldloca.s V_1 + IL_000b: ldloca.s V_2 + IL_000d: ldc.i4.4 + IL_000e: ldc.i4.1 + IL_000f: ldloc.0 + IL_0010: ldobj ""StructLogger"" + IL_0015: call ""DummyHandler..ctor(int, int, StructLogger)"" + IL_001a: ldloca.s V_2 + IL_001c: ldstr ""log:"" + IL_0021: call ""void DummyHandler.AppendLiteral(string)"" + IL_0026: ldloca.s V_2 + IL_0028: ldc.i4.0 + IL_0029: call ""void DummyHandler.AppendFormatted(int)"" + IL_002e: ldloc.2 + IL_002f: call ""void StructLogger.Log(DummyHandler)"" + IL_0034: ret +} +"); + } + [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")]