diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index ab30354fe3622..b623bd371ddd8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CSharp.CodeGen; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -493,6 +494,17 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind } } + if (expr.Kind == BoundKind.UnconvertedSwitchExpression && + expr.Type is not null && + valueKind is BindValueKind.RValue or BindValueKind.Assignable) + { + var switchExpression = (BoundUnconvertedSwitchExpression)expr; + if (switchExpression.IsRef) + { + expr = ConvertSwitchExpression(switchExpression, expr.Type, null, diagnostics); + } + } + if (!hasResolutionErrors && CheckValueKind(expr.Syntax, expr, valueKind, checkingReceiver: false, diagnostics: diagnostics) || expr.HasAnyErrors && valueKind == BindValueKind.RValueOrMethodGroup) { @@ -550,6 +562,14 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.EventAccess: return CheckEventValueKind((BoundEventAccess)expr, valueKind, diagnostics); + + case BoundKind.UnconvertedSwitchExpression: + case BoundKind.ConvertedSwitchExpression: + bool check = CheckSwitchExpressionValueKind((BoundSwitchExpression)expr, valueKind, diagnostics); + if (!check) + return false; + + break; } // easy out for a very common RValue case. @@ -566,6 +586,8 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin return false; } + var errorSpan = node.Span; + switch (expr.Kind) { case BoundKind.NamespaceExpression: @@ -789,13 +811,52 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin // Strict RValue break; + case BoundKind.UnconvertedSwitchExpression: + case BoundKind.ConvertedSwitchExpression: + var switchExpression = (BoundSwitchExpression)expr; + + var switchExpressionNode = (CSharp.Syntax.SwitchExpressionSyntax)switchExpression.Syntax; + errorSpan = TextSpan.FromBounds(switchExpressionNode.SpanStart, switchExpressionNode.SwitchKeyword.Span.End); + + if (switchExpression.IsRef) + { + Debug.Assert(switchExpression.SwitchArms.Length > 0, "By-ref switch expressions must always have at least one switch arm"); + + // defer check to the switch arms' values + bool check = true; + foreach (var arm in switchExpression.SwitchArms) + { + // Specially handle throw expressions in arms because they are not treated the same elsewhere + if (arm.Value is BoundThrowExpression or BoundConversion { Operand: BoundThrowExpression }) + continue; + + check &= CheckValueKind(arm.Value.Syntax, arm.Value, valueKind, checkingReceiver: false, diagnostics: diagnostics); + } + if (check) + return true; + break; + } + else + { + if (RequiresReferenceToLocation(valueKind)) + { + // We use a different error to better hint the user about the feature + var switchExpressionErrorLocation = Location.Create(node.SyntaxTree, errorSpan); + Error(diagnostics, ErrorCode.ERR_RefOnNonRefSwitchExpression, switchExpressionErrorLocation); + return false; + } + } + + return true; + default: Debug.Assert(expr is not BoundValuePlaceholderBase, $"Placeholder kind {expr.Kind} should be explicitly handled"); break; } // At this point we should have covered all the possible cases for anything that is not a strict RValue. - Error(diagnostics, GetStandardLvalueError(valueKind), node); + var errorLocation = Location.Create(node.SyntaxTree, errorSpan); + Error(diagnostics, GetStandardLvalueError(valueKind), errorLocation); return false; bool checkArrayAccessValueKind(SyntaxNode node, BindValueKind valueKind, ImmutableArray indices, BindingDiagnosticBag diagnostics) @@ -1128,14 +1189,14 @@ private bool CheckParameterRefEscape(SyntaxNode node, BoundExpression parameter, { (checkingReceiver: true, isRefScoped: true, inUnsafeRegion: false, _) => (ErrorCode.ERR_RefReturnScopedParameter2, parameter.Syntax), (checkingReceiver: true, isRefScoped: true, inUnsafeRegion: true, _) => (ErrorCode.WRN_RefReturnScopedParameter2, parameter.Syntax), - (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: false, ReturnOnlyScope) => (ErrorCode.ERR_RefReturnOnlyParameter2, parameter.Syntax), - (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: true, ReturnOnlyScope) => (ErrorCode.WRN_RefReturnOnlyParameter2, parameter.Syntax), + (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: false, ReturnOnlyScope) => (ErrorCode.ERR_RefReturnOnlyParameter2, parameter.Syntax), + (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: true, ReturnOnlyScope) => (ErrorCode.WRN_RefReturnOnlyParameter2, parameter.Syntax), (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: false, _) => (ErrorCode.ERR_RefReturnParameter2, parameter.Syntax), (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: true, _) => (ErrorCode.WRN_RefReturnParameter2, parameter.Syntax), (checkingReceiver: false, isRefScoped: true, inUnsafeRegion: false, _) => (ErrorCode.ERR_RefReturnScopedParameter, node), (checkingReceiver: false, isRefScoped: true, inUnsafeRegion: true, _) => (ErrorCode.WRN_RefReturnScopedParameter, node), - (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: false, ReturnOnlyScope) => (ErrorCode.ERR_RefReturnOnlyParameter, node), - (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: true, ReturnOnlyScope) => (ErrorCode.WRN_RefReturnOnlyParameter, node), + (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: false, ReturnOnlyScope) => (ErrorCode.ERR_RefReturnOnlyParameter, node), + (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: true, ReturnOnlyScope) => (ErrorCode.WRN_RefReturnOnlyParameter, node), (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: false, _) => (ErrorCode.ERR_RefReturnParameter, node), (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: true, _) => (ErrorCode.WRN_RefReturnParameter, node) }; @@ -1353,6 +1414,61 @@ private bool CheckFieldLikeEventRefEscape(SyntaxNode node, BoundEventAccess even } } + partial class RefSafetyAnalysis + { + private void ValidateRefSwitchExpression(SyntaxNode node, ImmutableArray arms, BindingDiagnosticBag diagnostics) + { + var currentScope = _localScopeDepth; + + var expressionEscapes = PooledHashSet<(BoundExpression expression, uint escape)>.GetInstance(); + + bool hasSameEscapes = true; + uint minEscape = uint.MaxValue; + + // val-escape must agree on all arms + foreach (var arm in arms) + { + var expression = arm.Value; + + if (expression is BoundConversion conversion) + { + Debug.Assert(conversion is { Operand: BoundThrowExpression }); + continue; + } + + uint expressionEscape = GetValEscape(expression, currentScope); + if (expressionEscapes.Count > 0) + { + if (minEscape != expressionEscape) + { + hasSameEscapes = false; + } + } + minEscape = Math.Min(minEscape, expressionEscape); + expressionEscapes.Add((expression, expressionEscape)); + } + + if (!hasSameEscapes) + { + // pass through all the expressions whose value escape was calculated + // ask the ones with narrower escape, for the wider + foreach (var expressionEscape in expressionEscapes) + { + var (expression, escape) = expressionEscape; + if (escape != minEscape) + { + Debug.Assert(escape > minEscape); + CheckValEscape(expression.Syntax, expression, currentScope, minEscape, checkingReceiver: false, diagnostics: diagnostics); + } + } + + diagnostics.Add(_inUnsafeRegion ? ErrorCode.WRN_MismatchedRefEscapeInSwitchExpression : ErrorCode.ERR_MismatchedRefEscapeInSwitchExpression, node.Location); + } + + expressionEscapes.Free(); + } + } + internal partial class Binder { private bool CheckEventValueKind(BoundEventAccess boundEvent, BindValueKind valueKind, BindingDiagnosticBag diagnostics) @@ -1495,6 +1611,31 @@ protected bool CheckMethodReturnValueKind( } + private bool CheckSwitchExpressionValueKind(BoundSwitchExpression expression, BindValueKind valueKind, BindingDiagnosticBag diagnostics) + { + Debug.Assert(expression is not null); + + switch (valueKind) + { + case BindValueKind.CompoundAssignment: + if (!expression.IsRef) + { + Error(diagnostics, ErrorCode.ERR_RequiresRefReturningSwitchExpression, expression.Syntax); + return false; + } + return true; + case BindValueKind.RValue: + if (expression.IsRef) + { + Error(diagnostics, ErrorCode.ERR_UnusedSwitchExpressionRef, expression.Syntax); + return false; + } + return true; + } + + return true; + } + private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) { // SPEC: If the left operand is a property or indexer access, the property or indexer must @@ -3037,6 +3178,30 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres // otherwise it is an RValue break; + case BoundKind.UnconvertedSwitchExpression: + throw ExceptionUtilities.UnexpectedValue(expr.Kind); + + case BoundKind.ConvertedSwitchExpression: + var switchExpression = (BoundConvertedSwitchExpression)expr; + + if (switchExpression.IsRef) + { + uint maxScope = uint.MinValue; + foreach (var arm in switchExpression.SwitchArms) + { + if (arm.Value is BoundConversion boundConversion) + { + Debug.Assert(boundConversion is { Operand: BoundThrowExpression }); + continue; + } + + maxScope = Math.Max(GetRefEscape(arm.Value, scopeOfTheContainingExpression), maxScope); + } + return maxScope; + } + + break; + case BoundKind.FieldAccess: return GetFieldRefEscape((BoundFieldAccess)expr, scopeOfTheContainingExpression); @@ -3549,6 +3714,17 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.ThrowExpression: return true; + + case BoundKind.ConvertedSwitchExpression: + case BoundKind.UnconvertedSwitchExpression: + var switchExpression = (BoundSwitchExpression)expr; + foreach (var arm in switchExpression.SwitchArms) + { + bool canEscape = CheckRefEscape(node, arm.Value, escapeFrom, escapeTo, checkingReceiver: false, diagnostics); + if (!canEscape) + return false; + } + return true; } // At this point we should have covered all the possible cases for anything that is not a strict RValue. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index bc6a36f9cfd7c..46f4d9c85f0f8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -951,23 +951,29 @@ private BoundExpression ConvertSwitchExpression(BoundUnconvertedSwitchExpression Debug.Assert(targetTyped || destination.IsErrorType() || destination.Equals(source.Type, TypeCompareKind.ConsiderEverything)); ImmutableArray underlyingConversions = conversion.UnderlyingConversions; var builder = ArrayBuilder.GetInstance(source.SwitchArms.Length); + bool allowConversion = !source.IsRef; for (int i = 0, n = source.SwitchArms.Length; i < n; i++) { - var oldCase = source.SwitchArms[i]; - var oldValue = oldCase.Value; - var newValue = - targetTyped - ? CreateConversion(oldValue.Syntax, oldValue, underlyingConversions[i], isCast: false, conversionGroupOpt: null, destination, diagnostics) - : GenerateConversionForAssignment(destination, oldValue, diagnostics); - var newCase = (oldValue == newValue) ? oldCase : - new BoundSwitchExpressionArm(oldCase.Syntax, oldCase.Locals, oldCase.Pattern, oldCase.WhenClause, newValue, oldCase.Label, oldCase.HasErrors); - builder.Add(newCase); + var oldArm = source.SwitchArms[i]; + var oldValue = oldArm.Value; + var newValue = oldValue; + + bool requiresConversion = oldValue.Type is null; + if (allowConversion || requiresConversion) + { + newValue = targetTyped + ? CreateConversion(oldValue.Syntax, oldValue, underlyingConversions[i], isCast: false, conversionGroupOpt: null, destination, diagnostics) + : GenerateConversionForAssignment(destination, oldValue, diagnostics); + } + var newArm = (oldValue == newValue) ? oldArm : + new BoundSwitchExpressionArm(oldArm.Syntax, oldArm.Locals, oldArm.Pattern, oldArm.WhenClause, newValue, oldArm.Label, oldArm.RefKind, oldArm.HasErrors); + builder.Add(newArm); } var newSwitchArms = builder.ToImmutableAndFree(); return new BoundConvertedSwitchExpression( source.Syntax, source.Type, targetTyped, source.Expression, newSwitchArms, source.ReachabilityDecisionDag, - source.DefaultLabel, source.ReportedNotExhaustive, destination, hasErrors || source.HasErrors).WithSuppression(source.IsSuppressed); + source.DefaultLabel, source.ReportedNotExhaustive, source.RefKind, destination, hasErrors || source.HasErrors).WithSuppression(source.IsSuppressed); } private BoundExpression CreateUserDefinedConversion( diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 55d0cf6970813..eec4ba99d69bd 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -109,6 +109,20 @@ private BoundExpression BindCompoundAssignment(AssignmentExpressionSyntax node, leftPlaceholder: null, leftConversion: null, finalPlaceholder: null, finalConversion: null, LookupResultKind.NotAVariable, CreateErrorType(), hasErrors: true); } + if (left.Kind == BoundKind.UnconvertedSwitchExpression) + { + var switchExpression = (BoundUnconvertedSwitchExpression)left; + var switchExpressionDiagnostics = diagnostics; + if (!switchExpression.IsRef) + { + Error(diagnostics, ErrorCode.ERR_RequiresRefReturningSwitchExpression, node.OperatorToken); + // Ignore further binding errors, potentially including unavailable conversions + switchExpressionDiagnostics = BindingDiagnosticBag.Discarded; + } + + left = this.ConvertSwitchExpression(switchExpression, destination: left.Type, null, switchExpressionDiagnostics); + } + // A compound operator, say, x |= y, is bound as x = (X)( ((T)x) | ((T)y) ). We must determine // the binary operator kind, the type conversions from each side to the types expected by // the operator, and the type conversion from the return type of the operand to the left hand side. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 7614c81b52a0e..6beecf4cc616c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1941,7 +1941,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, } else { - return expression; + return ConvertIdentityRefExpression(expression, targetType, diagnostics); } } else if (!conversion.IsValid || @@ -1965,6 +1965,19 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, return CreateConversion(expression.Syntax, expression, conversion, isCast: false, conversionGroupOpt: null, targetType, diagnostics); } + private BoundExpression ConvertIdentityRefExpression(BoundExpression expression, TypeSymbol destination, BindingDiagnosticBag diagnostics) + { + switch (expression.Kind) + { + case BoundKind.UnconvertedSwitchExpression: + var switchExpression = (BoundUnconvertedSwitchExpression)expression; + return ConvertSwitchExpression(switchExpression, destination, null, diagnostics); + + default: + return expression; + } + } + #nullable enable private static Location GetAnonymousFunctionLocation(SyntaxNode node) => node switch diff --git a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index 534b328284053..734502e8ac801 100644 --- a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -396,6 +396,10 @@ private void AssertVisited(BoundExpression expr) this.Visit(node.Expression); using var _ = new PatternInput(this, GetValEscape(node.Expression, _localScopeDepth)); this.VisitList(node.SwitchArms); + if (node.IsRef) + { + this.ValidateRefSwitchExpression(node.Syntax, node.SwitchArms, this._diagnostics); + } return null; } diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs index 62406a5a774bf..9462f15da1c1b 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs @@ -43,9 +43,23 @@ internal override BoundSwitchExpressionArm BindSwitchExpressionArm(SwitchExpress BoundExpression? whenClause = node.WhenClause != null ? armBinder.BindBooleanExpression(node.WhenClause.Condition, diagnostics) : null; - BoundExpression armResult = armBinder.BindValue(node.Expression, diagnostics, BindValueKind.RValue); + + var bindValueKind = BindValueKind.RValue; + var unwrappedExpression = node.Expression.CheckAndUnwrapRefExpression(diagnostics, out var refKind); + + if (refKind is not RefKind.None) + { + var refExpression = node.Expression as RefExpressionSyntax; + Debug.Assert(refExpression is not null); + var refKeywordLocation = Location.Create(node.SyntaxTree, refExpression!.RefKeyword.Span); + MessageID.IDS_FeatureRefInSwitchExpressionArm.CheckFeatureAvailability(diagnostics, node, refKeywordLocation); + + bindValueKind |= BindValueKind.RefersToLocation; + } + + BoundExpression armResult = armBinder.BindValue(unwrappedExpression, diagnostics, bindValueKind); var label = new GeneratedLabelSymbol("arm"); - return new BoundSwitchExpressionArm(node, locals, pattern, whenClause, armResult, label, hasErrors | pattern.HasErrors); + return new BoundSwitchExpressionArm(node, locals, pattern, whenClause, armResult, label, refKind, hasErrors | pattern.HasErrors); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs index 73ff46d298ef9..ab379aa55bea2 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs @@ -30,16 +30,37 @@ internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSynta // Bind switch expression and set the switch governing type. var boundInputExpression = BindSwitchGoverningExpression(diagnostics); ImmutableArray switchArms = BindSwitchExpressionArms(node, originalBinder, boundInputExpression, diagnostics); - TypeSymbol? naturalType = InferResultType(switchArms, diagnostics); + TypeSymbol? naturalType = InferResultType(switchArms, out var refKind, diagnostics); bool reportedNotExhaustive = CheckSwitchExpressionExhaustive(node, boundInputExpression, switchArms, out BoundDecisionDag decisionDag, out LabelSymbol? defaultLabel, diagnostics); + CheckSwitchExpressionRefInArms(refKind, switchArms, diagnostics); + // When the input is constant, we use that to reshape the decision dag that is returned // so that flow analysis will see that some of the cases may be unreachable. decisionDag = decisionDag.SimplifyDecisionDagIfConstantInput(boundInputExpression); return new BoundUnconvertedSwitchExpression( node, boundInputExpression, switchArms, decisionDag, - defaultLabel: defaultLabel, reportedNotExhaustive: reportedNotExhaustive, type: naturalType); + defaultLabel, reportedNotExhaustive, refKind, type: naturalType); + } + + private void CheckSwitchExpressionRefInArms( + RefKind refKind, + ImmutableArray switchArms, + BindingDiagnosticBag diagnostics) + { + if (refKind == RefKind.Ref) + { + foreach (var arm in switchArms) + { + if (arm.RefKind is not RefKind.Ref && arm.Value.Kind is not BoundKind.ThrowExpression) + { + Debug.Assert(arm.Syntax is SwitchExpressionArmSyntax); + var armSyntax = (SwitchExpressionArmSyntax)arm.Syntax; + diagnostics.Add(ErrorCode.ERR_MissingRefInSwitchExpressionArm, armSyntax.EqualsGreaterThanToken.GetLocation()); + } + } + } } /// @@ -52,12 +73,12 @@ internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSynta /// /// true if there was a non-exhaustive warning reported private bool CheckSwitchExpressionExhaustive( - SwitchExpressionSyntax node, - BoundExpression boundInputExpression, - ImmutableArray switchArms, - out BoundDecisionDag decisionDag, - [NotNullWhen(true)] out LabelSymbol? defaultLabel, - BindingDiagnosticBag diagnostics) + SwitchExpressionSyntax node, + BoundExpression boundInputExpression, + ImmutableArray switchArms, + out BoundDecisionDag decisionDag, + [NotNullWhen(true)] out LabelSymbol? defaultLabel, + BindingDiagnosticBag diagnostics) { defaultLabel = new GeneratedLabelSymbol("default"); decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchExpression(this.Compilation, node, boundInputExpression, switchArms, defaultLabel, diagnostics); @@ -136,17 +157,25 @@ static void addNonNullSuccessors(ref TemporaryArray builde /// Infer the result type of the switch expression by looking for a common type /// to which every arm's expression can be converted. /// - private TypeSymbol? InferResultType(ImmutableArray switchCases, BindingDiagnosticBag diagnostics) + private TypeSymbol? InferResultType(ImmutableArray switchArms, out RefKind refKind, BindingDiagnosticBag diagnostics) { + refKind = RefKind.None; var seenTypes = Symbols.SpecializedSymbolCollections.GetPooledSymbolHashSetInstance(); var typesInOrder = ArrayBuilder.GetInstance(); - foreach (var @case in switchCases) + foreach (var arm in switchArms) { - var type = @case.Value.Type; - if (type is object && seenTypes.Add(type)) + var type = arm.Value.Type; + if (type is not null && seenTypes.Add(type)) { typesInOrder.Add(type); } + + switch (arm.RefKind) + { + case RefKind.Ref: + refKind = RefKind.Ref; + break; + } } seenTypes.Free(); @@ -154,13 +183,22 @@ static void addNonNullSuccessors(ref TemporaryArray builde var commonType = BestTypeInferrer.GetBestType(typesInOrder, Conversions, ref useSiteInfo); typesInOrder.Free(); - // We've found a candidate common type among those arms that have a type. Also check that every arm's - // expression (even those without a type) can be converted to that type. - if (commonType is object) + // We've found a candidate common type among those arms that have a type. + // Also check that every arm's expression (even those without a type) can be converted to that type. + // If the arm is a ref return, ensure that the type is equal to the common type, without accounting for conversions. + if (commonType is not null) { - foreach (var @case in switchCases) + foreach (var arm in switchArms) { - if (!this.Conversions.ClassifyImplicitConversionFromExpression(@case.Value, commonType, ref useSiteInfo).Exists) + if (arm.IsRef) + { + if (!commonType.Equals(arm.Value.Type)) + { + commonType = null; + break; + } + } + else if (!this.Conversions.ClassifyImplicitConversionFromExpression(arm.Value, commonType, ref useSiteInfo).Exists) { commonType = null; break; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 880528b447fe1..6d487a0cb5b5e 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1480,6 +1480,7 @@ + @@ -1487,6 +1488,7 @@ + diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundSwitchExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundSwitchExpression.cs index d86b6cddf4900..cb0b256acf1ae 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundSwitchExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundSwitchExpression.cs @@ -9,6 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp { internal partial class BoundSwitchExpression { + public bool IsRef => RefKind is not RefKind.None; + public BoundDecisionDag GetDecisionDagForLowering(CSharpCompilation compilation, out LabelSymbol? defaultLabel) { defaultLabel = this.DefaultLabel; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundSwitchExpressionArm.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundSwitchExpressionArm.cs new file mode 100644 index 0000000000000..1923f5dfe8a64 --- /dev/null +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundSwitchExpressionArm.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal partial class BoundSwitchExpressionArm + { + public bool IsRef => RefKind is not RefKind.None; + } +} diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 900b86787b2f5..436279adc256f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7793,4 +7793,28 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Ref field should be ref-assigned before use. + + ref in switch expression arms + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + The switch expression in the left side of a compound assignment must return a by-ref expression + + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + + The reference of a ref-returning switch expression may not be discarded + + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index e3ddae5048f25..45fb07f4cf14e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2271,6 +2271,17 @@ internal enum ErrorCode #endregion + #region diagnostics introduced for C# Next + + ERR_MismatchedRefEscapeInSwitchExpression = 9300, + WRN_MismatchedRefEscapeInSwitchExpression = 9301, + ERR_RequiresRefReturningSwitchExpression = 9302, + ERR_MissingRefInSwitchExpressionArm = 9303, + ERR_UnusedSwitchExpressionRef = 9304, + ERR_RefOnNonRefSwitchExpression = 9305, + + #endregion + // Note: you will need to do the following after adding warnings: // 1) Re-generate compiler code (eng\generate-compiler-code.cmd). } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 6f27f87a94143..a61300c0bf0d2 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -551,6 +551,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_TargetDifferentRefness: case ErrorCode.WRN_RefReadonlyParameterDefaultValue: case ErrorCode.WRN_UseDefViolationRefField: + case ErrorCode.WRN_MismatchedRefEscapeInSwitchExpression: return 1; default: return 0; @@ -2399,6 +2400,12 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.WRN_ByValArraySizeConstRequired: case ErrorCode.WRN_UseDefViolationRefField: case ErrorCode.ERR_FeatureNotAvailableInVersion12: + case ErrorCode.ERR_MismatchedRefEscapeInSwitchExpression: + case ErrorCode.WRN_MismatchedRefEscapeInSwitchExpression: + case ErrorCode.ERR_RequiresRefReturningSwitchExpression: + case ErrorCode.ERR_MissingRefInSwitchExpressionArm: + case ErrorCode.ERR_UnusedSwitchExpressionRef: + case ErrorCode.ERR_RefOnNonRefSwitchExpression: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index bcbe7b69c7bfa..b976913c4bca5 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -276,6 +276,8 @@ internal enum MessageID IDS_FeatureInlineArrays = MessageBase + 12836, IDS_FeatureCollectionExpressions = MessageBase + 12837, IDS_FeatureRefReadonlyParameters = MessageBase + 12838, + + IDS_FeatureRefInSwitchExpressionArm = MessageBase + 12860, } // Message IDs may refer to strings that need to be localized. @@ -456,8 +458,8 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // PREFER reporting diagnostics in binding when diagnostics do not affect the shape of the syntax tree // C# preview features. - //case MessageID.IDS_NewFeature: - // return LanguageVersion.Preview; + case MessageID.IDS_FeatureRefInSwitchExpressionArm: // syntax check + return LanguageVersion.Preview; // C# 12.0 features. case MessageID.IDS_FeatureLambdaOptionalParameters: // semantic check diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 5587fada10819..c79b9f29b0970 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -608,6 +608,7 @@ protected void VisitLvalue(BoundExpression node) VisitLvalue(access); break; } + default: VisitRvalue(node); break; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs index 1a9e95047fcdd..6b38dc8436a25 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs @@ -174,7 +174,17 @@ private BoundNode VisitSwitchExpression(BoundSwitchExpression node) SetState(StateWhenTrue); } - VisitRvalue(arm.Value); + if (arm.IsRef) + { + VisitLvalue(arm.Value); + // exposing ref is a potential write + WriteArgument(arm.Value, RefKind.Ref, method: null); + } + else + { + VisitRvalue(arm.Value); + } + Join(ref endState, ref this.State); } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index a80983f7ab02c..632f288356e29 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -4772,7 +4772,7 @@ public BoundConditionalGoto Update(BoundExpression condition, bool jumpIfTrue, L internal abstract partial class BoundSwitchExpression : BoundExpression { - protected BoundSwitchExpression(BoundKind kind, SyntaxNode syntax, BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, TypeSymbol? type, bool hasErrors = false) + protected BoundSwitchExpression(BoundKind kind, SyntaxNode syntax, BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, RefKind refKind, TypeSymbol? type, bool hasErrors = false) : base(kind, syntax, type, hasErrors) { @@ -4785,6 +4785,7 @@ protected BoundSwitchExpression(BoundKind kind, SyntaxNode syntax, BoundExpressi this.ReachabilityDecisionDag = reachabilityDecisionDag; this.DefaultLabel = defaultLabel; this.ReportedNotExhaustive = reportedNotExhaustive; + this.RefKind = refKind; } public BoundExpression Expression { get; } @@ -4792,11 +4793,12 @@ protected BoundSwitchExpression(BoundKind kind, SyntaxNode syntax, BoundExpressi public BoundDecisionDag ReachabilityDecisionDag { get; } public LabelSymbol? DefaultLabel { get; } public bool ReportedNotExhaustive { get; } + public RefKind RefKind { get; } } internal sealed partial class BoundSwitchExpressionArm : BoundNode { - public BoundSwitchExpressionArm(SyntaxNode syntax, ImmutableArray locals, BoundPattern pattern, BoundExpression? whenClause, BoundExpression value, LabelSymbol label, bool hasErrors = false) + public BoundSwitchExpressionArm(SyntaxNode syntax, ImmutableArray locals, BoundPattern pattern, BoundExpression? whenClause, BoundExpression value, LabelSymbol label, RefKind refKind, bool hasErrors = false) : base(BoundKind.SwitchExpressionArm, syntax, hasErrors || pattern.HasErrors() || whenClause.HasErrors() || value.HasErrors()) { @@ -4810,6 +4812,7 @@ public BoundSwitchExpressionArm(SyntaxNode syntax, ImmutableArray l this.WhenClause = whenClause; this.Value = value; this.Label = label; + this.RefKind = refKind; } public ImmutableArray Locals { get; } @@ -4817,15 +4820,16 @@ public BoundSwitchExpressionArm(SyntaxNode syntax, ImmutableArray l public BoundExpression? WhenClause { get; } public BoundExpression Value { get; } public LabelSymbol Label { get; } + public RefKind RefKind { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSwitchExpressionArm(this); - public BoundSwitchExpressionArm Update(ImmutableArray locals, BoundPattern pattern, BoundExpression? whenClause, BoundExpression value, LabelSymbol label) + public BoundSwitchExpressionArm Update(ImmutableArray locals, BoundPattern pattern, BoundExpression? whenClause, BoundExpression value, LabelSymbol label, RefKind refKind) { - if (locals != this.Locals || pattern != this.Pattern || whenClause != this.WhenClause || value != this.Value || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(label, this.Label)) + if (locals != this.Locals || pattern != this.Pattern || whenClause != this.WhenClause || value != this.Value || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(label, this.Label) || refKind != this.RefKind) { - var result = new BoundSwitchExpressionArm(this.Syntax, locals, pattern, whenClause, value, label, this.HasErrors); + var result = new BoundSwitchExpressionArm(this.Syntax, locals, pattern, whenClause, value, label, refKind, this.HasErrors); result.CopyAttributes(this); return result; } @@ -4835,8 +4839,8 @@ public BoundSwitchExpressionArm Update(ImmutableArray locals, Bound internal sealed partial class BoundUnconvertedSwitchExpression : BoundSwitchExpression { - public BoundUnconvertedSwitchExpression(SyntaxNode syntax, BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, TypeSymbol? type, bool hasErrors = false) - : base(BoundKind.UnconvertedSwitchExpression, syntax, expression, switchArms, reachabilityDecisionDag, defaultLabel, reportedNotExhaustive, type, hasErrors || expression.HasErrors() || switchArms.HasErrors() || reachabilityDecisionDag.HasErrors()) + public BoundUnconvertedSwitchExpression(SyntaxNode syntax, BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, RefKind refKind, TypeSymbol? type, bool hasErrors = false) + : base(BoundKind.UnconvertedSwitchExpression, syntax, expression, switchArms, reachabilityDecisionDag, defaultLabel, reportedNotExhaustive, refKind, type, hasErrors || expression.HasErrors() || switchArms.HasErrors() || reachabilityDecisionDag.HasErrors()) { RoslynDebug.Assert(expression is object, "Field 'expression' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -4849,11 +4853,11 @@ public BoundUnconvertedSwitchExpression(SyntaxNode syntax, BoundExpression expre [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitUnconvertedSwitchExpression(this); - public BoundUnconvertedSwitchExpression Update(BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, TypeSymbol? type) + public BoundUnconvertedSwitchExpression Update(BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, RefKind refKind, TypeSymbol? type) { - if (expression != this.Expression || switchArms != this.SwitchArms || reachabilityDecisionDag != this.ReachabilityDecisionDag || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(defaultLabel, this.DefaultLabel) || reportedNotExhaustive != this.ReportedNotExhaustive || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (expression != this.Expression || switchArms != this.SwitchArms || reachabilityDecisionDag != this.ReachabilityDecisionDag || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(defaultLabel, this.DefaultLabel) || reportedNotExhaustive != this.ReportedNotExhaustive || refKind != this.RefKind || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundUnconvertedSwitchExpression(this.Syntax, expression, switchArms, reachabilityDecisionDag, defaultLabel, reportedNotExhaustive, type, this.HasErrors); + var result = new BoundUnconvertedSwitchExpression(this.Syntax, expression, switchArms, reachabilityDecisionDag, defaultLabel, reportedNotExhaustive, refKind, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -4863,8 +4867,8 @@ public BoundUnconvertedSwitchExpression Update(BoundExpression expression, Immut internal sealed partial class BoundConvertedSwitchExpression : BoundSwitchExpression { - public BoundConvertedSwitchExpression(SyntaxNode syntax, TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.ConvertedSwitchExpression, syntax, expression, switchArms, reachabilityDecisionDag, defaultLabel, reportedNotExhaustive, type, hasErrors || expression.HasErrors() || switchArms.HasErrors() || reachabilityDecisionDag.HasErrors()) + public BoundConvertedSwitchExpression(SyntaxNode syntax, TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, RefKind refKind, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.ConvertedSwitchExpression, syntax, expression, switchArms, reachabilityDecisionDag, defaultLabel, reportedNotExhaustive, refKind, type, hasErrors || expression.HasErrors() || switchArms.HasErrors() || reachabilityDecisionDag.HasErrors()) { RoslynDebug.Assert(expression is object, "Field 'expression' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -4883,11 +4887,11 @@ public BoundConvertedSwitchExpression(SyntaxNode syntax, TypeSymbol? naturalType [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitConvertedSwitchExpression(this); - public BoundConvertedSwitchExpression Update(TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, TypeSymbol type) + public BoundConvertedSwitchExpression Update(TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundExpression expression, ImmutableArray switchArms, BoundDecisionDag reachabilityDecisionDag, LabelSymbol? defaultLabel, bool reportedNotExhaustive, RefKind refKind, TypeSymbol type) { - if (!TypeSymbol.Equals(naturalTypeOpt, this.NaturalTypeOpt, TypeCompareKind.ConsiderEverything) || wasTargetTyped != this.WasTargetTyped || expression != this.Expression || switchArms != this.SwitchArms || reachabilityDecisionDag != this.ReachabilityDecisionDag || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(defaultLabel, this.DefaultLabel) || reportedNotExhaustive != this.ReportedNotExhaustive || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (!TypeSymbol.Equals(naturalTypeOpt, this.NaturalTypeOpt, TypeCompareKind.ConsiderEverything) || wasTargetTyped != this.WasTargetTyped || expression != this.Expression || switchArms != this.SwitchArms || reachabilityDecisionDag != this.ReachabilityDecisionDag || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(defaultLabel, this.DefaultLabel) || reportedNotExhaustive != this.ReportedNotExhaustive || refKind != this.RefKind || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundConvertedSwitchExpression(this.Syntax, naturalTypeOpt, wasTargetTyped, expression, switchArms, reachabilityDecisionDag, defaultLabel, reportedNotExhaustive, type, this.HasErrors); + var result = new BoundConvertedSwitchExpression(this.Syntax, naturalTypeOpt, wasTargetTyped, expression, switchArms, reachabilityDecisionDag, defaultLabel, reportedNotExhaustive, refKind, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -11367,7 +11371,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern); BoundExpression? whenClause = (BoundExpression?)this.Visit(node.WhenClause); BoundExpression value = (BoundExpression)this.Visit(node.Value); - return node.Update(node.Locals, pattern, whenClause, value, node.Label); + return node.Update(node.Locals, pattern, whenClause, value, node.Label, node.RefKind); } public override BoundNode? VisitUnconvertedSwitchExpression(BoundUnconvertedSwitchExpression node) { @@ -11375,7 +11379,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor ImmutableArray switchArms = this.VisitList(node.SwitchArms); BoundDecisionDag reachabilityDecisionDag = node.ReachabilityDecisionDag; TypeSymbol? type = this.VisitType(node.Type); - return node.Update(expression, switchArms, reachabilityDecisionDag, node.DefaultLabel, node.ReportedNotExhaustive, type); + return node.Update(expression, switchArms, reachabilityDecisionDag, node.DefaultLabel, node.ReportedNotExhaustive, node.RefKind, type); } public override BoundNode? VisitConvertedSwitchExpression(BoundConvertedSwitchExpression node) { @@ -11384,7 +11388,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundDecisionDag reachabilityDecisionDag = node.ReachabilityDecisionDag; TypeSymbol? naturalTypeOpt = this.VisitType(node.NaturalTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(naturalTypeOpt, node.WasTargetTyped, expression, switchArms, reachabilityDecisionDag, node.DefaultLabel, node.ReportedNotExhaustive, type); + return node.Update(naturalTypeOpt, node.WasTargetTyped, expression, switchArms, reachabilityDecisionDag, node.DefaultLabel, node.ReportedNotExhaustive, node.RefKind, type); } public override BoundNode? VisitDecisionDag(BoundDecisionDag node) { @@ -13438,7 +13442,7 @@ public NullabilityRewriter(ImmutableDictionary arm.Syntax), - generateInstrumentation: !node.WasCompilerGenerated && localRewriter.Instrument) + generateInstrumentation: !node.WasCompilerGenerated && localRewriter.Instrument && !node.IsRef) { } @@ -86,7 +91,10 @@ private BoundExpression LowerSwitchExpression(BoundConvertedSwitchExpression nod // decision tree, so the code in result is unreachable at this point. // Lower each switch expression arm - LocalSymbol resultTemp = _factory.SynthesizedLocal(node.Type, node.Syntax, kind: SynthesizedLocalKind.LoweringTemp); + + bool isRef = node.IsRef; + var localKind = isRef ? SynthesizedLocalKind.SwitchExpressionRef : SynthesizedLocalKind.LoweringTemp; + LocalSymbol resultTemp = _factory.SynthesizedLocal(node.Type, node.Syntax, kind: localKind, refKind: node.RefKind); LabelSymbol afterSwitchExpression = _factory.GenerateLabel("afterSwitchExpression"); foreach (BoundSwitchExpressionArm arm in node.SwitchArms) { @@ -98,7 +106,7 @@ private BoundExpression LowerSwitchExpression(BoundConvertedSwitchExpression nod if (GenerateInstrumentation) loweredValue = this._localRewriter.Instrumenter.InstrumentSwitchExpressionArmExpression(arm.Value, loweredValue, _factory); - sectionBuilder.Add(_factory.Assignment(_factory.Local(resultTemp), loweredValue)); + sectionBuilder.Add(_factory.Assignment(_factory.Local(resultTemp), loweredValue, isRef)); sectionBuilder.Add(_factory.Goto(afterSwitchExpression)); var statements = sectionBuilder.ToImmutableAndFree(); if (arm.Locals.IsEmpty) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index 468b6dea2fc3b..d8b60234fbf65 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -609,7 +609,7 @@ private SeparatedSyntaxList ParseSwitchExpressionArms this.CurrentToken.Kind == SyntaxKind.ColonToken ? this.EatTokenAsKind(SyntaxKind.EqualsGreaterThanToken) : this.EatToken(SyntaxKind.EqualsGreaterThanToken), - ParseExpressionCore()); + ParsePossibleRefExpression()); // If we're not making progress, abort if (switchExpressionCase.Width == 0 && this.CurrentToken.Kind != SyntaxKind.CommaToken) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index b963d6824421c..22254d1aba87c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1237,6 +1237,11 @@ Žádná přetížená metoda {0} neodpovídá ukazateli na funkci {1}. + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Neočekávané klíčové slovo record. Měli jste na mysli record struct nebo record class? @@ -1262,6 +1267,11 @@ Chybějící vzor + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. Inicializátor modulu nemůže mít atribut UnmanagedCallersOnly. @@ -1617,6 +1627,11 @@ Levá strana přiřazení odkazu musí být parametr Ref. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ Typy a aliasy nemůžou mít název required. + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' {0}: Cílový modul runtime nepodporuje v přepisech kovariantní typy. Typ musí být {2}, aby odpovídal přepsanému členu {1}. @@ -2042,6 +2062,11 @@ Neukončený literál nezpracovaného řetězce. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Použití potenciálně nepřiřazeného pole {0}. Zvažte aktualizaci jazykové verze {1} na automatické výchozí nastavení pole. @@ -2222,6 +2247,11 @@ pole ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Převádí se skupina metod na nedelegující typ. + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes Větve podmíněného operátoru odkazu odkazují na proměnné s nekompatibilními obory deklarací diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index dcdd844bb810c..2754e040f7ad1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1237,6 +1237,11 @@ Keine Überladung für "{0}" stimmt mit dem Funktionszeiger "{1}" überein. + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Unerwartetes Schlüsselwort „Datensatz“. Meinten Sie „Datensatzstruktur“ oder „Datensatzklasse“? @@ -1262,6 +1267,11 @@ Muster fehlt. + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. Der Modulinitialisierer kann nicht mit dem Attribut "UnmanagedCallersOnly" versehen werden. @@ -1617,6 +1627,11 @@ Die linke Seite einer Ref-Zuweisung muss eine Ref-Variable sein. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ Typen und Aliase können nicht als "erforderlich" bezeichnet werden. + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' {0}: Die Zielruntime unterstützt keine covarianten Typen in Überschreibungen. Der Typ muss "{2}" sein, um dem überschriebenen Member "{1}" zu entsprechen. @@ -2042,6 +2062,11 @@ Nicht abgeschlossenes Zeichenfolgenliteral. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Verwendung eines möglicherweise nicht zugewiesenen Felds '{0}'. Erwägen Sie eine Aktualisierung auf die Sprachversion '{1}', um das Feld automatisch als Standard zu verwenden. @@ -2222,6 +2247,11 @@ Referenzfelder + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Die Methodengruppe wird in einen Nichtdelegattyp konvertiert. + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes Die Branches des bedingten ref-Operators verweisen auf Variablen mit inkompatiblen Deklarationsbereichen. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 768721f7a5bfa..cdf7f4b8370c5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1237,6 +1237,11 @@ Ninguna sobrecarga correspondiente a "{0}" coincide con el puntero de función "{1}". + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Palabra clave \"record\" inesperada. ¿Quería decir \"record struct\" o \"record class\"? @@ -1262,6 +1267,11 @@ Falta un patrón. + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. No se puede atribuir el inicializador de módulo con "UnmanagedCallersOnly". @@ -1617,6 +1627,11 @@ La parte izquierda de una asignación de referencias debe ser una variable local. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ Los tipos y alias no se pueden denominar 'required'. + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' "{0}": el entorno de ejecución de destino no admite los tipos de covariante en las invalidaciones. El tipo debe ser "{2}" para que coincida con el miembro "{1}" invalidado. @@ -2042,6 +2062,11 @@ Literal de cadena sin formato sin terminar. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Uso del campo posiblemente sin asignar '{0}'. Considere la posibilidad de actualizar a la versión de idioma "{1}" para establecer automáticamente el valor predeterminado del campo. @@ -2222,6 +2247,11 @@ campos de referencia + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Convirtiendo grupo de métodos a tipo no delegado + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes Las ramas del operador condicional ref hacen referencia a variables con ámbitos de declaración incompatibles diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 73dd6ccbc662a..97551229a2012 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1237,6 +1237,11 @@ Aucune surcharge pour '{0}' ne correspond au pointeur de fonction '{1}' + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Mot clé 'record' inattendu. Vouliez-vous dire « struct d’enregistrement » ou « classe d’enregistrement » ? @@ -1262,6 +1267,11 @@ Modèle manquant + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. L'initialiseur de module ne peut pas être attribué avec 'UnmanagedCallersOnly'. @@ -1617,6 +1627,11 @@ Le côté gauche d’une affectation ref doit être une variable ref. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ Les types et alias ne peuvent pas être nommés « required ». + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' '{0}' : le runtime cible ne prend pas en charge les types covariants dans les substitutions. Le type doit être '{2}' pour correspondre au membre substitué '{1}' @@ -2042,6 +2062,11 @@ Littéral de chaîne brute inachevé. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Utilisation d’un '{0}' de champ éventuellement non attribué. Envisagez de mettre à jour vers la version de langage '{1}' pour utiliser la valeur par défaut automatique du champ. @@ -2222,6 +2247,11 @@ champs ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Conversion d’un groupe de méthodes en type non-délégué + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes Les branches d'un opérateur conditionnel ref font référence à des variables ayant des étendues de déclaration incompatibles diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index eab23bdefbd64..2d90d175d4ec0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1237,6 +1237,11 @@ Nessun overload per '{0}' corrisponde al puntatore a funzione '{1}' + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Parola chiave 'record' imprevista. Si intendeva 'struct record' o 'classe record'? @@ -1262,6 +1267,11 @@ Criterio mancante + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. Non è possibile aggiungere all'inizializzatore di modulo l'attributo 'UnmanagedCallersOnly'. @@ -1617,6 +1627,11 @@ La parte sinistra di un'assegnazione ref deve essere una variabile ref. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ I tipi e gli alias non possono essere denominati 'obbligatori'. + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' '{0}': il runtime di destinazione non supporta tipi covarianti negli override. Il tipo deve essere '{2}' in modo da corrispondere al membro '{1}' di cui è stato eseguito l'override @@ -2042,6 +2062,11 @@ Valore letterale stringa senza terminazione. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Uso del campo probabilmente non assegnato '{0}'. Provare a eseguire l'aggiornamento alla versione del linguaggio '{1}' per impostare automaticamente il campo come predefinito. @@ -2222,6 +2247,11 @@ campi ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Conversione del gruppo di metodi in un tipo non delegato + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes I rami dell'operatore condizionale di riferimento fanno riferimento a variabili con ambiti di dichiarazione incompatibili diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 08b203c304bd5..a5800b6e8f951 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1237,6 +1237,11 @@ 関数ポインター '{1}' に一致する '{0}' のオーバーロードはありません + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? 予期しないキーワード 'record' です。'record struct' または 'record class' のつもりでしたか? @@ -1262,6 +1267,11 @@ パターンがありません + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. モジュール初期化子に 'UnmanagedCallersOnly' 属性を設定することはできません。 @@ -1617,6 +1627,11 @@ ref 代入の左辺は ref 変数である必要があります。 + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ 型とエイリアスに 'required' という名前を付けることはできません。 + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' '{0}': ターゲットのランタイムはオーバーライドで covariant 型をサポートしていません。型は、オーバーライドされるメンバー '{1}' と一致する '{2}' にする必要があります @@ -2042,6 +2062,11 @@ 生文字列リテラルが終了していません。 + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. 割り当てられていない可能性のあるフィールド '{0}' を使用しています。フィールドを自動既定値にするため '{1}' 言語バージョンに更新することを検討してください。 @@ -2222,6 +2247,11 @@ ref フィールド + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ メソッド グループを非デリゲート型に変換しています + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes ref 条件演算子のブランチでは、互換性のない宣言スコープを持つ変数を参照します diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index be5a0be86926a..a5efc85329143 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1237,6 +1237,11 @@ 함수 포인터 '{1}'과(와) 일치하는 '{0}'에 대한 오버로드가 없습니다. + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? 예기치 않은 'record' 키워드가 있습니다. 'record struct' 또는 'record class'를 사용할까요? @@ -1262,6 +1267,11 @@ 패턴이 없습니다. + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. 모듈 이니셜라이저에는 'UnmanagedCallersOnly' 특성을 지정할 수 없습니다. @@ -1617,6 +1627,11 @@ ref 할당의 왼쪽은 ref 변수여야 합니다. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ 유형 및 별칭은 '필수'로 지정할 수 없습니다. + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' '{0}': 대상 런타임이 재정의에서 공변(covariant) 형식을 지원하지 않습니다. 재정의된 멤버 '{1}'과(와) 일치하려면 '{2}' 형식이어야 합니다. @@ -2042,6 +2062,11 @@ 종결되지 않은 원시 문자열 리터럴입니다. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. 할당되지 않은 필드 '{0}'을(를) 사용하고 있습니다. 필드를 자동으로 기본 설정하려면 언어 버전 '{1}'으로 업데이트하는 것이 좋습니다. @@ -2222,6 +2247,11 @@ 참조 필드 + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ 메소드 그룹을 비 위임 유형으로 변환 + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes ref 조건부 연산자의 분기는 선언 범위가 호환되지 않는 변수를 참조합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 1f17145256d9b..e5a47ba64bb66 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1237,6 +1237,11 @@ Żadne z przeciążeń dla elementu „{0}” nie pasuje do wskaźnika funkcji „{1}” + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Nieoczekiwane słowo kluczowe „record”. Czy chodziło o „record struct” lub „record class”? @@ -1262,6 +1267,11 @@ Brak wzorca + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. Inicjator modułu nie może mieć atrybutu „UnmanagedCallersOnly”. @@ -1617,6 +1627,11 @@ Lewa strona przypisania referencyjnego musi być zmienną referencyjną. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ Typy i aliasy nie mogą mieć nazwy „required”. + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' „{0}”: docelowe środowisko uruchomieniowe nie obsługuje typów kowariantnych w przesłonięciach. Typem musi być „{2}”, aby zachować zgodność z przesłoniętą składową „{1}”. @@ -2042,6 +2062,11 @@ Niezakończony literał ciągu znaków. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Użycie prawdopodobnie nieprzypisanego pola „{0}”. Rozważ zaktualizowanie pola do wersji językowej „{1}”, aby automatycznie ustawić domyślne pole. @@ -2222,6 +2247,11 @@ pola referencyjne + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Konwertowanie grupy metod na typ inny niż delegowany + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes Gałęzie operatora warunkowego ref nie mogą przywoływać zmiennych z niezgodnymi zakresami deklaracji diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 8272cfad344ae..afb6fae825cf6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1237,6 +1237,11 @@ Nenhuma sobrecarga de '{0}' corresponde ao ponteiro de função '{1}' + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Palavra-chave inesperada “record”. Você quis dizer “record struct” or “record class”? @@ -1262,6 +1267,11 @@ Padrão ausente + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. O inicializador de módulo não pode ser atribuído com 'UnmanagedCallersOnly'. @@ -1617,6 +1627,11 @@ O lado esquerdo de uma atribuição ref deve ser uma variável ref. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ Tipos e pseudônimos não podem ser nomeados 'requeridos'. + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' '{0}': o runtime de destino não dá suporte a tipos covariantes em substituições. O tipo precisa ser '{2}' para corresponder ao membro substituído '{1}' @@ -2042,6 +2062,11 @@ Literal de cadeia de caracteres não terminada. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Uso de campo possivelmente não atribuído '{0}'. Considere atualizar para a versão de linguagem '{1}' para auto-padrão do campo. @@ -2222,6 +2247,11 @@ campos ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Convertendo grupo de método em tipo não delegado + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes As ramificações do operador condicional ref referem-se a variáveis com escopos de declaração incompatíveis diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 66f6a8bea6518..89000384350fa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1237,6 +1237,11 @@ Нет перегруженного метода для "{0}", который соответствует указателю на функцию "{1}". + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Непредвиденное ключевое слово \"record\". Возможно, вы имели в виду \"record struct\" или \"record class\"? @@ -1262,6 +1267,11 @@ Отсутствует шаблон + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. Инициализатор модуля не может иметь атрибут "UnmanagedCallersOnly". @@ -1617,6 +1627,11 @@ Левая сторона назначения ref должна быть переменной ref. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ У типов и псевдонимов не может быть имя "required". + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' "{0}": целевая среда выполнения не поддерживает ковариантные типы в переопределениях. Для сопоставления переопределенного элемента "{1}" необходимо использовать тип "{2}". @@ -2042,6 +2062,11 @@ Незавершенный литерал необработанной строки. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Используется поле "{0}", которое может быть не назначено. Попробуйте обновить до языковой версии "{1}", чтобы автоматически применить значения по умолчанию к этому полю. @@ -2222,6 +2247,11 @@ поля ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Преобразование группы методов в незаменямый тип + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes Ветви условного оператора ref ссылаются на переменные с несовместимыми областями объявления. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index e9776e42d05ff..5c175ba5e099f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1237,6 +1237,11 @@ '{0}' için aşırı yüklemelerin hiçbiri '{1}' işlev işaretçisiyle eşleşmiyor + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? Beklenmeyen 'record' anahtar sözcüğü. 'record struct' veya 'record class' mi demek istediniz? @@ -1262,6 +1267,11 @@ Desen eksik + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. Modül başlatıcısı 'UnmanagedCallersOnly' ile ilişkilendirilemez. @@ -1617,6 +1627,11 @@ ref atamasının sol tarafı, ref değişkeni olmalıdır. + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ Türler ve diğer adlar 'gerekli' olarak adlandırılamaz. + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' '{0}': Hedef çalışma zamanı, geçersiz kılmalarda birlikte değişken türleri desteklemiyor. Tür, geçersiz kılınan '{1}' üyesiyle eşleşmek için '{2}' olmalıdır @@ -2042,6 +2062,11 @@ Sonlandırılmamış ham dize sabit değeri. + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. Atanmamış olabilecek '{0}' alanının kullanımı. Alanı otomatik olarak varsayılan durumuna getirmek için '{1}' dil sürümüne güncelleştirmeyi düşünün. @@ -2222,6 +2247,11 @@ başvuru alanları + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ Yöntem grubunu temsilci olmayan türe dönüştürme + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes Ref koşullu operatörünün dalları, uyumsuz bildirim kapsamlarına sahip değişkenlere başvurur diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 30aca150bacf1..96d3c14adf325 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1237,6 +1237,11 @@ “{0}”没有与函数指针“{1}”匹配的重载 + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? 意外的关键字 \"record\"。你的意思是 \"record struct\" 还是 \"record class\"? @@ -1262,6 +1267,11 @@ 模式缺失 + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. 无法使用 "UnmanagedCallersOnly" 对模块初始值设定项进行特性化。 @@ -1617,6 +1627,11 @@ ref 赋值的左侧必须为 ref 变量。 + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ 类型和别名不能命名为 “required”。 + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' “{0}”: 目标运行时不支持替代中的协变类型。类型必须为“{2}”才能匹配替代成员“{1}” @@ -2042,6 +2062,11 @@ 未终止的字符串字面量。 + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. 使用可能未分配的字段 '{0}'。请考虑更新到语言版本 '{1}' 以自动默认字段。 @@ -2222,6 +2247,11 @@ ref 字段 + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ 将方法组转换为非委托类型 + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes ref 条件运算符的分支引用具有不兼容声明范围的变量 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 07cb993e09668..0925692e56ca6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1237,6 +1237,11 @@ '{0}' 沒有任何多載符合函式指標 '{1}' + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? 未預期的關鍵字 'record'。您是指 'record struct' 或 'record class'? @@ -1262,6 +1267,11 @@ 缺少模式 + + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + + Module initializer cannot be attributed with 'UnmanagedCallersOnly'. 無法使用 'UnmanagedCallersOnly' 將模組初始設定式屬性化。 @@ -1617,6 +1627,11 @@ 參考指派的左側必須為 ref 變數。 + + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + + 'readonly' modifier must be specified after 'ref'. 'readonly' modifier must be specified after 'ref'. @@ -1717,6 +1732,11 @@ 類型和別名不能命名為 'required'。 + + The switch expression in the left side of a compound assignment must return a by-ref expression + The switch expression in the left side of a compound assignment must return a by-ref expression + + '{0}': Target runtime doesn't support covariant types in overrides. Type must be '{2}' to match overridden member '{1}' '{0}': 在覆寫中,目標執行階段不支援 Covariant 類型。類型必須是 '{2}',才符合覆寫的成員 '{1}' @@ -2042,6 +2062,11 @@ 未結束的原始字串常值。 + + The reference of a ref-returning switch expression may not be discarded + The reference of a ref-returning switch expression may not be discarded + + Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field. 使用可能未指派的欄位 '{0}'。請考慮更新語言版本 '{1}' 以自動預設欄位。 @@ -2222,6 +2247,11 @@ ref 欄位 + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters @@ -2652,6 +2682,16 @@ 將方法群組轉換為非委派類型 + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + + + The branches of the ref switch expression refer to variables with incompatible declaration scopes + The branches of the ref switch expression refer to variables with incompatible declaration scopes + + The branches of the ref conditional operator refer to variables with incompatible declaration scopes Ref 條件運算子的分支參考具有不相容宣告範圍的變數 diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests3.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests3.cs index 1be33c211fbeb..3731dc29e03c5 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests3.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests3.cs @@ -521,36 +521,10 @@ ref int M(bool b, ref int x, ref int y) }"; var compilation = CreateCompilationWithMscorlibAndSpan(source, options: TestOptions.DebugDll); compilation.VerifyDiagnostics( - // (6,21): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // (6,21): error CS9305: The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? // return ref (b switch { true => x, false => y }); - Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "b switch { true => x, false => y }").WithLocation(6, 21)); - } - - [Fact] - public void NoRefSwitch_02() - { - var source = @" -class Program -{ - ref int M(bool b, ref int x, ref int y) - { - return ref (b switch { true => ref x, false => ref y }); - } -}"; - var compilation = CreateCompilationWithMscorlibAndSpan(source, options: TestOptions.DebugDll); - compilation.VerifyDiagnostics( - // (6,40): error CS1525: Invalid expression term 'ref' - // return ref (b switch { true => ref x, false => ref y }); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref x").WithArguments("ref").WithLocation(6, 40), - // (6,40): error CS1073: Unexpected token 'ref' - // return ref (b switch { true => ref x, false => ref y }); - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(6, 40), - // (6,56): error CS1525: Invalid expression term 'ref' - // return ref (b switch { true => ref x, false => ref y }); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref y").WithArguments("ref").WithLocation(6, 56), - // (6,56): error CS1073: Unexpected token 'ref' - // return ref (b switch { true => ref x, false => ref y }); - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(6, 56)); + Diagnostic(ErrorCode.ERR_RefOnNonRefSwitchExpression, "b switch").WithLocation(6, 21) + ); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index cd4bfec204243..4b348eb93f84f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -6234,17 +6234,17 @@ .locals init (string V_0, public void SwitchTypes_03(string expression) { // Same 02, but with a target-type. The natural type will fail to compile, so the switch will use a target type (unlike TernaryTypes_03, which fails to compile). - var code = @" + var code = $$""" using System; -CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; +CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => {{expression}} }; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator string(CustomHandler c) => c.ToString(); } -"; +"""; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs index c6cd7ca1a6d26..2209e80fff23a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs @@ -4515,17 +4515,17 @@ .locals init (string V_0, public void SwitchTypes_03(string expression) { // Same 02, but with a target-type. The natural type will fail to compile, so the switch will use a target type (unlike TernaryTypes_03, which fails to compile). - var code = @" + var code = $$""" using System; -CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; +CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => {{expression}} }; Console.WriteLine(x); public partial struct CustomHandler { public static implicit operator string(CustomHandler c) => c.ToString(); } -"; +"""; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); VerifyInterpolatedStringExpression(comp); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 825e55fd10807..f00949e01f3b3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -4907,5 +4907,877 @@ public void RefLocalInOutVar_01() Assert.Equal("System.Int32", f.Type.ToTestDisplayString()); } } + + #region Ref switch expressions + + [Theory] + [InlineData("North")] + [InlineData("South")] + [InlineData("East")] + [InlineData("West")] + public void RefSwitchExpression_DirectReturnMethod(string direction) + { + string source = + $$""" + public class C + { + public int NorthField; + public int SouthField; + public int EastField; + public int WestField; + + public ref int GetDirectionField(Direction direction) => ref direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + _ => throw null!, + }; + + public ref int GetDirectionField_Target(Direction direction) + { + switch (direction) + { + case Direction.North: + return ref NorthField; + case Direction.South: + return ref SouthField; + case Direction.East: + return ref EastField; + case Direction.West: + return ref WestField; + default: + throw null!; + } + } + } + + public enum Direction + { + North, + South, + East, + West, + } + + public class Program + { + static void Main() + { + const int otherValue = 3412; + + var c = new C(); + ref var directionField = ref c.GetDirectionField(Direction.{{direction}}); + directionField = otherValue; + ref var directionField1 = ref c.GetDirectionField(Direction.{{direction}}); + System.Console.Write($"{directionField1 is otherValue} {directionField} {directionField1}"); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics(); + + // Skipped verification because of method returning by ref int + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: "True 3412 3412"); + + const string targetIL = + """ + { + // Code size 69 (0x45) + .maxstack 1 + .locals init (Direction V_0, + Direction V_1, + int& V_2) + // sequence point: { + IL_0000: nop + // sequence point: switch (direction) + IL_0001: ldarg.1 + IL_0002: stloc.1 + // sequence point: + IL_0003: ldloc.1 + IL_0004: stloc.0 + // sequence point: + IL_0005: ldloc.0 + IL_0006: switch ( + IL_001d, + IL_0026, + IL_002f, + IL_0038) + IL_001b: br.s IL_0041 + // sequence point: return ref NorthField; + IL_001d: ldarg.0 + IL_001e: ldflda "int C.NorthField" + IL_0023: stloc.2 + IL_0024: br.s IL_0043 + // sequence point: return ref SouthField; + IL_0026: ldarg.0 + IL_0027: ldflda "int C.SouthField" + IL_002c: stloc.2 + IL_002d: br.s IL_0043 + // sequence point: return ref EastField; + IL_002f: ldarg.0 + IL_0030: ldflda "int C.EastField" + IL_0035: stloc.2 + IL_0036: br.s IL_0043 + // sequence point: return ref WestField; + IL_0038: ldarg.0 + IL_0039: ldflda "int C.WestField" + IL_003e: stloc.2 + IL_003f: br.s IL_0043 + // sequence point: throw null!; + IL_0041: ldnull + IL_0042: throw + // sequence point: } + IL_0043: ldloc.2 + IL_0044: ret + } + """; + + const string expressionIL = + """ + { + // Code size 64 (0x40) + .maxstack 1 + .locals init (int& V_0) + // sequence point: direction sw ... } + IL_0000: ldarg.1 + IL_0001: switch ( + IL_0018, + IL_0021, + IL_002a, + IL_0033) + IL_0016: br.s IL_003c + IL_0018: ldarg.0 + IL_0019: ldflda "int C.NorthField" + IL_001e: stloc.0 + IL_001f: br.s IL_003e + IL_0021: ldarg.0 + IL_0022: ldflda "int C.SouthField" + IL_0027: stloc.0 + IL_0028: br.s IL_003e + IL_002a: ldarg.0 + IL_002b: ldflda "int C.EastField" + IL_0030: stloc.0 + IL_0031: br.s IL_003e + IL_0033: ldarg.0 + IL_0034: ldflda "int C.WestField" + IL_0039: stloc.0 + IL_003a: br.s IL_003e + IL_003c: ldnull + IL_003d: throw + IL_003e: ldloc.0 + IL_003f: ret + } + """; + + verifier.VerifyMethodBody("C.GetDirectionField_Target", targetIL); + verifier.VerifyMethodBody("C.GetDirectionField", expressionIL); + } + + [Theory] + [InlineData("North")] + [InlineData("South")] + [InlineData("East")] + [InlineData("West")] + public void RefSwitchExpression_LocalRefVariable(string direction) + { + string source = + $$""" + public class C + { + public int NorthField; + public int SouthField; + public int EastField; + public int WestField; + + public ref int GetDirectionField(Direction direction) + { + ref int directionVar = ref direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + _ => throw null!, + }; + + return ref directionVar; + } + } + + public enum Direction + { + North, + South, + East, + West, + } + + public class Program + { + static void Main() + { + const int otherValue = 3412; + + var c = new C(); + ref var directionField = ref c.GetDirectionField(Direction.{{direction}}); + directionField = otherValue; + ref var directionField1 = ref c.GetDirectionField(Direction.{{direction}}); + System.Console.Write($"{directionField1 is otherValue} {directionField} {directionField1}"); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + + // Skipped verification because of method returning by ref int + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: "True 3412 3412"); + + const string expressionIL = + """ + { + // Code size 71 (0x47) + .maxstack 1 + .locals init (int& V_0, //directionVar + int& V_1, + int& V_2) + // sequence point: { + IL_0000: nop + // sequence point: ref int dire ... }; + IL_0001: ldarg.1 + IL_0002: switch ( + IL_0019, + IL_0022, + IL_002b, + IL_0034) + IL_0017: br.s IL_003d + IL_0019: ldarg.0 + IL_001a: ldflda "int C.NorthField" + IL_001f: stloc.1 + IL_0020: br.s IL_003f + IL_0022: ldarg.0 + IL_0023: ldflda "int C.SouthField" + IL_0028: stloc.1 + IL_0029: br.s IL_003f + IL_002b: ldarg.0 + IL_002c: ldflda "int C.EastField" + IL_0031: stloc.1 + IL_0032: br.s IL_003f + IL_0034: ldarg.0 + IL_0035: ldflda "int C.WestField" + IL_003a: stloc.1 + IL_003b: br.s IL_003f + IL_003d: ldnull + IL_003e: throw + IL_003f: ldloc.1 + IL_0040: stloc.0 + // sequence point: return ref directionVar; + IL_0041: ldloc.0 + IL_0042: stloc.2 + IL_0043: br.s IL_0045 + // sequence point: } + IL_0045: ldloc.2 + IL_0046: ret + } + """; + + verifier.VerifyMethodBody("C.GetDirectionField", expressionIL); + } + + [Theory] + [InlineData("North")] + [InlineData("South")] + [InlineData("East")] + [InlineData("West")] + public void RefSwitchExpression_DirectAssignment(string direction) + { + string source = + $$""" + public class C + { + public int NorthField; + public int SouthField; + public int EastField; + public int WestField; + + public void SetDirectionField(Direction direction, int value) + { + direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + _ => throw null!, + } = value; + } + } + + public enum Direction + { + North, + South, + East, + West, + } + + public class Program + { + static void Main() + { + const int otherValue = 3412; + + var c = new C(); + c.SetDirectionField(Direction.{{direction}}, otherValue); + var value = c.{{direction}}Field; + System.Console.Write($"{value is otherValue} {value}"); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics(); + + // Skipped verification because of method returning by ref int + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: "True 3412"); + + const string expressionIL = + """ + { + // Code size 67 (0x43) + .maxstack 2 + .locals init (int& V_0) + // sequence point: { + IL_0000: nop + // sequence point: direction sw ... } = value + IL_0001: ldarg.1 + IL_0002: switch ( + IL_0019, + IL_0022, + IL_002b, + IL_0034) + IL_0017: br.s IL_003d + IL_0019: ldarg.0 + IL_001a: ldflda "int C.NorthField" + IL_001f: stloc.0 + IL_0020: br.s IL_003f + IL_0022: ldarg.0 + IL_0023: ldflda "int C.SouthField" + IL_0028: stloc.0 + IL_0029: br.s IL_003f + IL_002b: ldarg.0 + IL_002c: ldflda "int C.EastField" + IL_0031: stloc.0 + IL_0032: br.s IL_003f + IL_0034: ldarg.0 + IL_0035: ldflda "int C.WestField" + IL_003a: stloc.0 + IL_003b: br.s IL_003f + IL_003d: ldnull + IL_003e: throw + IL_003f: ldloc.0 + IL_0040: ldarg.2 + IL_0041: stind.i4 + // sequence point: } + IL_0042: ret + } + """; + + verifier.VerifyMethodBody("C.SetDirectionField", expressionIL); + } + + [Theory] + [InlineData("North")] + [InlineData("South")] + [InlineData("East")] + [InlineData("West")] + public void RefSwitchExpression_DirectCompoundAssignment(string direction) + { + string source = + $$""" + public class C + { + public int NorthField = 5; + public int SouthField = 6; + public int EastField = 10; + public int WestField = 12; + + public void IncrementDirectionField(Direction direction, int value) + { + direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + _ => throw null!, + } += value; + } + } + + public enum Direction + { + North, + South, + East, + West, + } + + public class Program + { + static void Main() + { + const int otherValue = 3412; + + var c = new C(); + var oldValue = c.{{direction}}Field; + c.IncrementDirectionField(Direction.{{direction}}, otherValue); + var expectedValue = oldValue + otherValue; + var value = c.{{direction}}Field; + System.Console.Write(value == expectedValue); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics(); + + // Skipped verification because of method returning by ref int + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: "True"); + + const string expressionIL = + """ + { + // Code size 72 (0x48) + .maxstack 3 + .locals init (int& V_0, + int& V_1) + // sequence point: { + IL_0000: nop + // sequence point: direction sw ... } += value + IL_0001: ldarg.1 + IL_0002: switch ( + IL_0019, + IL_0022, + IL_002b, + IL_0034) + IL_0017: br.s IL_003d + IL_0019: ldarg.0 + IL_001a: ldflda "int C.NorthField" + IL_001f: stloc.0 + IL_0020: br.s IL_003f + IL_0022: ldarg.0 + IL_0023: ldflda "int C.SouthField" + IL_0028: stloc.0 + IL_0029: br.s IL_003f + IL_002b: ldarg.0 + IL_002c: ldflda "int C.EastField" + IL_0031: stloc.0 + IL_0032: br.s IL_003f + IL_0034: ldarg.0 + IL_0035: ldflda "int C.WestField" + IL_003a: stloc.0 + IL_003b: br.s IL_003f + IL_003d: ldnull + IL_003e: throw + IL_003f: ldloc.0 + IL_0040: stloc.1 + IL_0041: ldloc.1 + IL_0042: ldloc.1 + IL_0043: ldind.i4 + IL_0044: ldarg.2 + IL_0045: add + IL_0046: stind.i4 + // sequence point: } + IL_0047: ret + } + """; + + verifier.VerifyMethodBody("C.IncrementDirectionField", expressionIL); + } + + [Fact] + public void RefSwitchExpression_RefEscapeWithStackalloc() + { + const string source = """ + using System; + + public class C + { + public unsafe ref int M(Axis axis, int index) + { + const int size = 5; + Span x = stackalloc int[size]; + Span y = stackalloc int[size]; + + return ref axis switch + { + Axis.X => ref x[index], + Axis.Y => ref y[index], + }; + } + } + + public enum Axis + { + X, + Y, + } + """; + + var comp = CreateCompilationWithSpan(source, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics( + // (11,25): warning CS8524: The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(Axis)2' is not covered. + // return ref axis switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveWithUnnamedEnumValue, "switch").WithArguments("(Axis)2").WithLocation(11, 25), + // (13,27): warning CS9080: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope + // Axis.X => ref x[index], + Diagnostic(ErrorCode.WRN_EscapeVariable, "x").WithArguments("x").WithLocation(13, 27), + // (14,27): warning CS9080: Use of variable 'y' in this context may expose referenced variables outside of their declaration scope + // Axis.Y => ref y[index], + Diagnostic(ErrorCode.WRN_EscapeVariable, "y").WithArguments("y").WithLocation(14, 27) + ); + } + + [Fact] + public void RefSwitchExpression_RefOverScopedVariables() + { + const string source = """ + public class C + { + public int NorthField; + public int SouthField; + public int EastField; + public int WestField; + + public unsafe ref int M(Direction direction, ref int input) + { + input = ref direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + _ => throw null!, + }; + + int outer = 10; + input = ref outer; + { + int inner = 10; + input = ref inner; + } + + return ref input; + } + } + + public enum Direction + { + North, + South, + East, + West, + } + """; + + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics( + // (20,9): warning CS9085: This ref-assigns 'outer' to 'input' but 'outer' has a narrower escape scope than 'input'. + // input = ref outer; + Diagnostic(ErrorCode.WRN_RefAssignNarrower, "input = ref outer").WithArguments("input", "outer").WithLocation(20, 9), + // (23,13): warning CS9085: This ref-assigns 'inner' to 'input' but 'inner' has a narrower escape scope than 'input'. + // input = ref inner; + Diagnostic(ErrorCode.WRN_RefAssignNarrower, "input = ref inner").WithArguments("input", "inner").WithLocation(23, 13) + ); + } + + [Fact] + public void RefSwitchExpression_Readonly01() + { + const string source = """ + public class C + { + public int NorthField; + public int SouthField; + public int EastField; + public int WestField; + + public unsafe ref int M(Direction direction, in int south, in int west) + { + const int east = 431; + + ref int x = ref direction switch + { + Direction.North => ref NorthField, + Direction.South => ref south, + Direction.East => ref EastField, + Direction.West => ref west, + _ => throw null!, + }; + + ref int xx = ref direction switch + { + Direction.North => ref NorthField, + Direction.South => ref south, + Direction.East => ref east, + Direction.West => ref west, + _ => throw null!, + }; + + ref readonly int y = ref direction switch + { + Direction.North => ref NorthField, + Direction.South => ref south, + Direction.East => ref east, + Direction.West => ref west, + _ => throw null!, + }; + + return ref y; + } + } + + public enum Direction + { + North, + South, + East, + West, + } + """; + + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics( + // (12,25): error CS1510: A ref or out value must be an assignable variable + // ref int x = ref direction switch + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "direction switch").WithLocation(12, 25), + // (15,36): error CS8329: Cannot use variable 'south' as a ref or out value because it is a readonly variable + // Direction.South => ref south, + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "south").WithArguments("variable", "south").WithLocation(15, 36), + // (17,35): error CS8329: Cannot use variable 'west' as a ref or out value because it is a readonly variable + // Direction.West => ref west, + Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "west").WithArguments("variable", "west").WithLocation(17, 35), + // (25,35): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // Direction.East => ref east, + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "east").WithLocation(25, 35), + // (34,35): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // Direction.East => ref east, + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "east").WithLocation(34, 35), + // (39,20): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return ref y; + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "y").WithLocation(39, 20) + ); + } + + [Fact] + public void RefSwitchExpression_UnusedRef() + { + const string source = """ + public class C + { + public int NorthField; + public int SouthField; + public int EastField; + public int WestField; + + public int M(Direction direction) + { + int x = direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + }; + + // Method invocation + A(direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + }); + + return x; + } + + private static void A(int x) { } + } + + public enum Direction + { + North, + South, + East, + West, + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics( + // (10,17): error CS9304: The reference of a ref-returning switch expression may not be discarded + // int x = direction switch + Diagnostic(ErrorCode.ERR_UnusedSwitchExpressionRef, @"direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + }").WithLocation(10, 17), + // (10,27): warning CS8524: The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(Direction)4' is not covered. + // int x = direction switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveWithUnnamedEnumValue, "switch").WithArguments("(Direction)4").WithLocation(10, 27), + // (19,11): error CS9304: The reference of a ref-returning switch expression may not be discarded + // A(direction switch + Diagnostic(ErrorCode.ERR_UnusedSwitchExpressionRef, @"direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, + }").WithLocation(19, 11), + // (19,21): warning CS8524: The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(Direction)4' is not covered. + // A(direction switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveWithUnnamedEnumValue, "switch").WithArguments("(Direction)4").WithLocation(19, 21) + ); + } + + [Fact] + public void RefSwitchExpression_MissingRefInArms() + { + const string source = """ + public class C + { + public int NorthField; + public int SouthField; + public int EastField; + public int WestField; + + public int M(Direction direction) + { + ref int x = ref direction switch + { + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => EastField, + Direction.West => WestField, + _ => throw null!, + }; + + return x; + } + } + + public enum Direction + { + North, + South, + East, + West, + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics( + // (14,28): error CS9303: The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + // Direction.East => EastField, + Diagnostic(ErrorCode.ERR_MissingRefInSwitchExpressionArm, "=>").WithLocation(14, 28), + // (15,28): error CS9303: The switch expression arm must return a by-ref expression, if at least one arm returns a by-ref expression + // Direction.West => WestField, + Diagnostic(ErrorCode.ERR_MissingRefInSwitchExpressionArm, "=>").WithLocation(15, 28) + ); + } + + [Fact] + public void RefSwitchExpression_RefInNonRefSwitch() + { + const string source = """ + public class C + { + public int NorthField; + public int SouthField; + public int EastField; + public int WestField; + + public int M(Direction direction) + { + ref int x = ref direction switch + { + Direction.North => NorthField, + Direction.South => SouthField, + Direction.East => EastField, + Direction.West => WestField, + _ => throw null!, + }; + + return x; + } + } + + public enum Direction + { + North, + South, + East, + West, + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyEmitDiagnostics( + // (10,25): error CS9305: The arms of the switch expression do not return a by-ref expression; did you mean to return the expressions by ref? + // ref int x = ref direction switch + Diagnostic(ErrorCode.ERR_RefOnNonRefSwitchExpression, "direction switch").WithLocation(10, 25) + ); + } + + [Fact] + public void RefSwitchExpression_UnavailableFeature() + { + const string source = """ + class C + { + public void M(int input) + { + int _1 = 1; + int _2 = 1; + int _3 = 1; + int _4 = 1; + + ref int x = ref input switch + { + 1 => ref _1, + 2 => ref _2, + 3 => ref _3, + 4 => ref _4, + _ => throw null!, + }; + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.Regular12); + comp.VerifyEmitDiagnostics( + // (12,18): error CS8652: The feature 'ref in switch expression arms' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // 1 => ref _1, + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref").WithArguments("ref in switch expression arms").WithLocation(12, 18), + // (13,18): error CS8652: The feature 'ref in switch expression arms' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // 2 => ref _2, + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref").WithArguments("ref in switch expression arms").WithLocation(13, 18), + // (14,18): error CS8652: The feature 'ref in switch expression arms' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // 3 => ref _3, + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref").WithArguments("ref in switch expression arms").WithLocation(14, 18), + // (15,18): error CS8652: The feature 'ref in switch expression arms' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // 4 => ref _4, + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref").WithArguments("ref in switch expression arms").WithLocation(15, 18) + ); + } + + #endregion } } diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 266119468adf9..ec96f182b4f8d 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -426,6 +426,7 @@ public void WarningLevel_2() case ErrorCode.WRN_RefReturnOnlyParameter2: case ErrorCode.WRN_RefAssignValEscapeWider: case ErrorCode.WRN_UseDefViolationRefField: + case ErrorCode.WRN_MismatchedRefEscapeInSwitchExpression: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; case ErrorCode.WRN_InvalidVersionFormat: diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs index cfc1c343f5936..5acce2b392b3c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs @@ -2515,4 +2515,114 @@ public void TestIncompleteSwitchExpression() } EOF(); } + + [Fact] + public void RefSwitchExpression_01() + { + const string source = + """ + ref int a = ref axis switch + { + Axis.X => ref x, + Axis.Y => ref y, + }; + """; + + UsingStatement(source); + + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "axis"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Axis"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Axis"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Y"); + } + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } } diff --git a/src/Compilers/Core/Portable/SynthesizedLocalKind.cs b/src/Compilers/Core/Portable/SynthesizedLocalKind.cs index 884016c619cd2..d1d655a0f3530 100644 --- a/src/Compilers/Core/Portable/SynthesizedLocalKind.cs +++ b/src/Compilers/Core/Portable/SynthesizedLocalKind.cs @@ -219,6 +219,11 @@ internal enum SynthesizedLocalKind /// LocalStoreTracker = 36, + /// + /// Temp created for the result of a switch expression returning by ref. + /// + SwitchExpressionRef = 37, + /// /// All values have to be less than or equal to /// ()