From c244ab0f6fb60f1ca78cac244063b97ca941f661 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 12 Aug 2023 23:09:45 +0300 Subject: [PATCH 01/19] Enable parsing ref switch arms --- .../Parser/LanguageParser_Patterns.cs | 2 +- .../Parsing/SwitchExpressionParsingTests.cs | 110 ++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) 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/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(); + } } From eee925bd9f30ec1bbdd25172b603f41c5a9533e9 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Mon, 14 Aug 2023 11:43:30 +0300 Subject: [PATCH 02/19] Implement basic case of ref switch expression --- .../Portable/Binder/Binder.ValueChecks.cs | 40 +++++ .../Portable/Binder/Binder_Conversions.cs | 26 +-- .../Binder/SwitchExpressionArmBinder.cs | 20 ++- .../Portable/Binder/SwitchExpressionBinder.cs | 46 +++-- .../CSharp/Portable/BoundTree/BoundNodes.xml | 2 + .../BoundTree/BoundSwitchExpression.cs | 2 + .../BoundTree/BoundSwitchExpressionArm.cs | 11 ++ .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/MessageID.cs | 6 +- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 7 +- .../FlowAnalysis/AbstractFlowPass_Switch.cs | 2 +- .../Generated/BoundNodes.xml.Generated.cs | 53 +++--- .../Lowering/LocalRewriter/LocalRewriter.cs | 3 + .../LocalRewriter_SwitchExpression.cs | 14 +- .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Semantics/RefLocalsAndReturnsTests.cs | 162 ++++++++++++++++++ .../Core/Portable/SynthesizedLocalKind.cs | 5 + 29 files changed, 414 insertions(+), 53 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/BoundTree/BoundSwitchExpressionArm.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index ab30354fe3622..76aea0c822955 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -789,6 +789,35 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin // Strict RValue break; + case BoundKind.UnconvertedSwitchExpression: + case BoundKind.ConvertedSwitchExpression: + var switchExpression = (BoundSwitchExpression)expr; + + 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) + continue; + + check &= CheckValueKind(arm.Value.Syntax, arm.Value, valueKind, checkingReceiver: false, diagnostics: diagnostics); + if (!check) + { + check = false; + break; + } + } + if (check) + return true; + } + + break; + default: Debug.Assert(expr is not BoundValuePlaceholderBase, $"Placeholder kind {expr.Kind} should be explicitly handled"); break; @@ -3549,6 +3578,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/SwitchExpressionArmBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs index 62406a5a774bf..c17b3eb06ab85 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs @@ -43,9 +43,25 @@ 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); + // TODO: Get whether the underlying expression is readonly + 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..487d6bbc3546d 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs @@ -30,7 +30,7 @@ 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); // When the input is constant, we use that to reshape the decision dag that is returned @@ -39,7 +39,7 @@ internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSynta return new BoundUnconvertedSwitchExpression( node, boundInputExpression, switchArms, decisionDag, - defaultLabel: defaultLabel, reportedNotExhaustive: reportedNotExhaustive, type: naturalType); + defaultLabel, reportedNotExhaustive, refKind, type: naturalType); } /// @@ -136,17 +136,32 @@ 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 + when refKind is RefKind.None: + refKind = RefKind.Ref; + break; + + case RefKind.RefReadOnlyParameter: + case RefKind.RefReadOnly: + // We ignore escape safety here, and only normalize to ref readonly + refKind = RefKind.RefReadOnly; + break; + } } seenTypes.Free(); @@ -154,13 +169,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..06f2e93a6b9bd 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7793,4 +7793,7 @@ 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 + \ No newline at end of file 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..3edc968d7f650 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -3198,6 +3198,11 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node private void VisitConditionalOperand(TLocalState state, BoundExpression operand, bool isByRef) { SetState(state); + VisitPotentialByRefExpression(operand, isByRef); + } + + protected void VisitPotentialByRefExpression(BoundExpression operand, bool isByRef) + { if (isByRef) { VisitLvalue(operand); @@ -3206,7 +3211,7 @@ private void VisitConditionalOperand(TLocalState state, BoundExpression operand, } else { - Visit(operand); + VisitRvalue(operand); } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs index 1a9e95047fcdd..1a3a6b5e6ab6c 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs @@ -174,7 +174,7 @@ private BoundNode VisitSwitchExpression(BoundSwitchExpression node) SetState(StateWhenTrue); } - VisitRvalue(arm.Value); + VisitPotentialByRefExpression(arm.Value, arm.IsRef); 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/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index b963d6824421c..12526b201ae90 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2222,6 +2222,11 @@ pole ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index dcdd844bb810c..2d5e97be22e2f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2222,6 +2222,11 @@ Referenzfelder + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 768721f7a5bfa..25e571e57456d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2222,6 +2222,11 @@ campos de referencia + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 73dd6ccbc662a..e8fb5dd6ffa2c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2222,6 +2222,11 @@ champs ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index eab23bdefbd64..907d032cdeb83 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2222,6 +2222,11 @@ campi ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 08b203c304bd5..4aa8544f15c85 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2222,6 +2222,11 @@ ref フィールド + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index be5a0be86926a..ea670a44b8cd0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2222,6 +2222,11 @@ 참조 필드 + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 1f17145256d9b..9fb2916c2fdfb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2222,6 +2222,11 @@ pola referencyjne + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 8272cfad344ae..8c02aa9cf9cad 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2222,6 +2222,11 @@ campos ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 66f6a8bea6518..33bce7044bb23 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2222,6 +2222,11 @@ поля ref + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index e9776e42d05ff..fe7a2068d7a42 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2222,6 +2222,11 @@ başvuru alanları + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 30aca150bacf1..8431dc579f2a6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2222,6 +2222,11 @@ ref 字段 + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 07cb993e09668..fd0da5326ca56 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2222,6 +2222,11 @@ ref 欄位 + + ref in switch expression arms + ref in switch expression arms + + ref readonly parameters ref readonly parameters diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 825e55fd10807..93cdb6c2939a5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -4907,5 +4907,167 @@ public void RefLocalInOutVar_01() Assert.Equal("System.Int32", f.Type.ToTestDisplayString()); } } + + [Fact] + public void RefSwitchStatement() + { + const string source = + """ + public class C + { + int northField; + int southField; + int eastField; + 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 directionFieldNorth = ref c.GetDirectionField(Direction.North); + directionFieldNorth = otherValue; + ref var directionFieldNorth1 = ref c.GetDirectionField(Direction.North); + System.Console.Write($"{directionFieldNorth1 is otherValue} {directionFieldNorth} {directionFieldNorth1}"); + } + } + """; + + 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); + } } } 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 /// () From 9bec48bd94ff025331065c142d2f8fea18a28011 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Tue, 15 Aug 2023 23:54:06 +0300 Subject: [PATCH 03/19] Implement local variable assignment --- .../Portable/Binder/Binder.ValueChecks.cs | 81 +++- .../Portable/Binder/Binder_Statements.cs | 16 +- .../Portable/Binder/RefSafetyAnalysis.cs | 4 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 7 + .../Generated/ErrorFacts.Generated.cs | 1 + .../Lowering/LocalRewriter/LocalRewriter.cs | 3 + .../Portable/xlf/CSharpResources.cs.xlf | 10 + .../Portable/xlf/CSharpResources.de.xlf | 10 + .../Portable/xlf/CSharpResources.es.xlf | 10 + .../Portable/xlf/CSharpResources.fr.xlf | 10 + .../Portable/xlf/CSharpResources.it.xlf | 10 + .../Portable/xlf/CSharpResources.ja.xlf | 10 + .../Portable/xlf/CSharpResources.ko.xlf | 10 + .../Portable/xlf/CSharpResources.pl.xlf | 10 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 + .../Portable/xlf/CSharpResources.ru.xlf | 10 + .../Portable/xlf/CSharpResources.tr.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 + .../Semantics/RefLocalsAndReturnsTests.cs | 375 ++++++++++++++++-- 21 files changed, 588 insertions(+), 35 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 76aea0c822955..08f53fceb7083 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -1157,14 +1157,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) }; @@ -1382,6 +1382,58 @@ 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 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) @@ -3066,6 +3118,27 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres // otherwise it is an RValue break; + case BoundKind.ConvertedSwitchExpression: + case BoundKind.UnconvertedSwitchExpression: + var switchExpression = (BoundSwitchExpression)expr; + + if (switchExpression.IsRef) + { + // ref conditional defers to its operands + uint maxScope = uint.MinValue; + foreach (var arm in switchExpression.SwitchArms) + { + if (arm.Value is BoundThrowExpression or BoundConversion { Operand: BoundThrowExpression }) + continue; + + maxScope = Math.Max(GetRefEscape(arm.Value, scopeOfTheContainingExpression), maxScope); + } + return maxScope; + } + + // otherwise it is an RValue + break; + case BoundKind.FieldAccess: return GetFieldRefEscape((BoundFieldAccess)expr, scopeOfTheContainingExpression); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 7614c81b52a0e..01db6a1f25018 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, ref useSiteInfo); } } else if (!conversion.IsValid || @@ -1965,6 +1965,20 @@ 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, ref CompoundUseSiteInfo useSiteInfo) + { + switch (expression.Kind) + { + case BoundKind.UnconvertedSwitchExpression: + var switchExpression = (BoundUnconvertedSwitchExpression)expression; + //var conversion = this.Conversions.ClassifyConversionFromExpression(expression, destination, isChecked: CheckOverflowAtRuntime, ref useSiteInfo); + 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/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 06f2e93a6b9bd..70b8956d94823 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7796,4 +7796,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 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 + \ 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..e4e93d932d4f7 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2271,6 +2271,13 @@ internal enum ErrorCode #endregion + #region diagnostics introduced for C# Next + + ERR_MismatchedRefEscapeInSwitchExpression = 9300, + WRN_MismatchedRefEscapeInSwitchExpression = 9301, + + #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/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index b32db505ccf83..1ed4e920809d9 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -334,6 +334,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_TargetDifferentRefness: case ErrorCode.WRN_RefReadonlyParameterDefaultValue: case ErrorCode.WRN_UseDefViolationRefField: + case ErrorCode.WRN_MismatchedRefEscapeInSwitchExpression: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index bd3fb16f686cd..d9b2accf1ffdd 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -990,6 +990,9 @@ internal static bool CanBePassedByReference(BoundExpression expr) // this[int], Slice(int, int), Substring(int, int) return false; + case BoundKind.UnconvertedSwitchExpression: + throw ExceptionUtilities.UnexpectedValue(expr.Kind); + case BoundKind.ConvertedSwitchExpression: return ((BoundConvertedSwitchExpression)expr).IsRef; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 12526b201ae90..a610c5ad424a4 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? @@ -2657,6 +2662,11 @@ 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 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 2d5e97be22e2f..e19857e2b0c09 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“? @@ -2657,6 +2662,11 @@ 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 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 25e571e57456d..ee02ffd864acf 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\"? @@ -2657,6 +2662,11 @@ 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 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 e8fb5dd6ffa2c..391627ab5f359 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 » ? @@ -2657,6 +2662,11 @@ 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 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 907d032cdeb83..142e3e8b714df 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'? @@ -2657,6 +2662,11 @@ 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 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 4aa8544f15c85..bb07cebed033c 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' のつもりでしたか? @@ -2657,6 +2662,11 @@ メソッド グループを非デリゲート型に変換しています + + 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 ea670a44b8cd0..50fc56f5728e7 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'를 사용할까요? @@ -2657,6 +2662,11 @@ 메소드 그룹을 비 위임 유형으로 변환 + + 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 9fb2916c2fdfb..5bbd45bc71e76 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”? @@ -2657,6 +2662,11 @@ 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 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 8c02aa9cf9cad..1a01f02fa6225 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”? @@ -2657,6 +2662,11 @@ 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 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 33bce7044bb23..67af68512efe8 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\"? @@ -2657,6 +2662,11 @@ Преобразование группы методов в незаменямый тип + + 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 fe7a2068d7a42..f92929c576fa2 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? @@ -2657,6 +2662,11 @@ 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 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 8431dc579f2a6..ef765febc9b03 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\"? @@ -2657,6 +2662,11 @@ 将方法组转换为非委托类型 + + 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 fd0da5326ca56..8a65061ad187b 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'? @@ -2657,6 +2662,11 @@ 將方法群組轉換為非委派類型 + + 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/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 93cdb6c2939a5..831520ed4324c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -4908,24 +4908,30 @@ public void RefLocalInOutVar_01() } } - [Fact] - public void RefSwitchStatement() - { - const string source = - """ + #region Ref switch statements + + [Theory] + [InlineData("North")] + [InlineData("South")] + [InlineData("East")] + [InlineData("West")] + public void RefSwitchStatement_DirectReturnMethod(string direction) + { + string source = + $$""" public class C { - int northField; - int southField; - int eastField; - int westField; + 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, + Direction.North => ref NorthField, + Direction.South => ref SouthField, + Direction.East => ref EastField, + Direction.West => ref WestField, _ => throw null!, }; @@ -4934,13 +4940,13 @@ public ref int GetDirectionField_Target(Direction direction) switch (direction) { case Direction.North: - return ref northField; + return ref NorthField; case Direction.South: - return ref southField; + return ref SouthField; case Direction.East: - return ref eastField; + return ref EastField; case Direction.West: - return ref westField; + return ref WestField; default: throw null!; } @@ -4962,10 +4968,10 @@ static void Main() const int otherValue = 3412; var c = new C(); - ref var directionFieldNorth = ref c.GetDirectionField(Direction.North); - directionFieldNorth = otherValue; - ref var directionFieldNorth1 = ref c.GetDirectionField(Direction.North); - System.Console.Write($"{directionFieldNorth1 is otherValue} {directionFieldNorth} {directionFieldNorth1}"); + 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}"); } } """; @@ -5000,24 +5006,24 @@ .locals init (Direction V_0, IL_002f, IL_0038) IL_001b: br.s IL_0041 - // sequence point: return ref northField; + // sequence point: return ref NorthField; IL_001d: ldarg.0 - IL_001e: ldflda "int C.northField" + IL_001e: ldflda "int C.NorthField" IL_0023: stloc.2 IL_0024: br.s IL_0043 - // sequence point: return ref southField; + // sequence point: return ref SouthField; IL_0026: ldarg.0 - IL_0027: ldflda "int C.southField" + IL_0027: ldflda "int C.SouthField" IL_002c: stloc.2 IL_002d: br.s IL_0043 - // sequence point: return ref eastField; + // sequence point: return ref EastField; IL_002f: ldarg.0 - IL_0030: ldflda "int C.eastField" + IL_0030: ldflda "int C.EastField" IL_0035: stloc.2 IL_0036: br.s IL_0043 - // sequence point: return ref westField; + // sequence point: return ref WestField; IL_0038: ldarg.0 - IL_0039: ldflda "int C.westField" + IL_0039: ldflda "int C.WestField" IL_003e: stloc.2 IL_003f: br.s IL_0043 // sequence point: throw null!; @@ -5029,6 +5035,215 @@ .locals init (Direction V_0, } """; + 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 RefSwitchStatement_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 RefSwitchStatement_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 = """ { @@ -5066,8 +5281,108 @@ .locals init (int& V_0) } """; - verifier.VerifyMethodBody("C.GetDirectionField_Target", targetIL); verifier.VerifyMethodBody("C.GetDirectionField", expressionIL); } + + [Theory] + [InlineData("North")] + [InlineData("South")] + [InlineData("East")] + [InlineData("West")] + public void RefSwitchStatement_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 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", expressionIL); + } + + #endregion } } From cd2ecb8fb2ad54a1dcab2940b8de6951bad13306 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Wed, 16 Aug 2023 10:18:02 +0300 Subject: [PATCH 04/19] Direct assignment to a ref switch expression --- .../Portable/Binder/Binder.ValueChecks.cs | 19 ++++-- .../Portable/Binder/Binder_Statements.cs | 1 - .../Portable/FlowAnalysis/AbstractFlowPass.cs | 1 + .../Semantics/RefLocalsAndReturnsTests.cs | 67 ++++++++++--------- 4 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 08f53fceb7083..2bda952900d98 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -493,6 +493,12 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind } } + if (expr.Kind == BoundKind.UnconvertedSwitchExpression && valueKind is BindValueKind.RValue or BindValueKind.Assignable) + { + var converted = this.ConvertSwitchExpression((BoundUnconvertedSwitchExpression)expr, expr.Type, null, diagnostics); + return converted; + } + if (!hasResolutionErrors && CheckValueKind(expr.Syntax, expr, valueKind, checkingReceiver: false, diagnostics: diagnostics) || expr.HasAnyErrors && valueKind == BindValueKind.RValueOrMethodGroup) { @@ -802,7 +808,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin 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) + if (arm.Value is BoundThrowExpression or BoundConversion { Operand: BoundThrowExpression }) continue; check &= CheckValueKind(arm.Value.Syntax, arm.Value, valueKind, checkingReceiver: false, diagnostics: diagnostics); @@ -3118,9 +3124,11 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres // otherwise it is an RValue break; - case BoundKind.ConvertedSwitchExpression: case BoundKind.UnconvertedSwitchExpression: - var switchExpression = (BoundSwitchExpression)expr; + throw ExceptionUtilities.UnexpectedValue(expr.Kind); + + case BoundKind.ConvertedSwitchExpression: + var switchExpression = (BoundConvertedSwitchExpression)expr; if (switchExpression.IsRef) { @@ -3128,8 +3136,11 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres uint maxScope = uint.MinValue; foreach (var arm in switchExpression.SwitchArms) { - if (arm.Value is BoundThrowExpression or BoundConversion { Operand: BoundThrowExpression }) + if (arm.Value is BoundConversion boundConversion) + { + Debug.Assert(boundConversion is { Operand: BoundThrowExpression }); continue; + } maxScope = Math.Max(GetRefEscape(arm.Value, scopeOfTheContainingExpression), maxScope); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 01db6a1f25018..5629948d43049 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1971,7 +1971,6 @@ private BoundExpression ConvertIdentityRefExpression(BoundExpression expression, { case BoundKind.UnconvertedSwitchExpression: var switchExpression = (BoundUnconvertedSwitchExpression)expression; - //var conversion = this.Conversions.ClassifyConversionFromExpression(expression, destination, isChecked: CheckOverflowAtRuntime, ref useSiteInfo); return ConvertSwitchExpression(switchExpression, destination, null, diagnostics); default: diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 3edc968d7f650..6586f22a05496 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/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 831520ed4324c..c8aeceda95edc 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -5247,41 +5247,46 @@ static void Main() const string expressionIL = """ { - // Code size 64 (0x40) - .maxstack 1 + // Code size 67 (0x43) + .maxstack 2 .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 + // 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.GetDirectionField", expressionIL); + verifier.VerifyMethodBody("C.SetDirectionField", expressionIL); } [Theory] From e10047a68622f0a48e2142a4b16af9857dc16541 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Wed, 16 Aug 2023 23:05:55 +0300 Subject: [PATCH 05/19] Implement compound assignment with ref switch LHS --- .../Portable/Binder/Binder.ValueChecks.cs | 30 +++++++- .../Portable/Binder/Binder_Operators.cs | 14 ++++ .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + ...ocalRewriter_CompoundAssignmentOperator.cs | 4 + .../Portable/xlf/CSharpResources.cs.xlf | 5 ++ .../Portable/xlf/CSharpResources.de.xlf | 5 ++ .../Portable/xlf/CSharpResources.es.xlf | 5 ++ .../Portable/xlf/CSharpResources.fr.xlf | 5 ++ .../Portable/xlf/CSharpResources.it.xlf | 5 ++ .../Portable/xlf/CSharpResources.ja.xlf | 5 ++ .../Portable/xlf/CSharpResources.ko.xlf | 5 ++ .../Portable/xlf/CSharpResources.pl.xlf | 5 ++ .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 ++ .../Portable/xlf/CSharpResources.ru.xlf | 5 ++ .../Portable/xlf/CSharpResources.tr.xlf | 5 ++ .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 ++ .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 ++ .../Semantics/RefLocalsAndReturnsTests.cs | 75 +++++++++++-------- 19 files changed, 157 insertions(+), 35 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 2bda952900d98..254361b6bcd85 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -495,8 +495,7 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind if (expr.Kind == BoundKind.UnconvertedSwitchExpression && valueKind is BindValueKind.RValue or BindValueKind.Assignable) { - var converted = this.ConvertSwitchExpression((BoundUnconvertedSwitchExpression)expr, expr.Type, null, diagnostics); - return converted; + return ConvertSwitchExpression((BoundUnconvertedSwitchExpression)expr, expr.Type, null, diagnostics); } if (!hasResolutionErrors && CheckValueKind(expr.Syntax, expr, valueKind, checkingReceiver: false, diagnostics: diagnostics) || @@ -556,6 +555,10 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.EventAccess: return CheckEventValueKind((BoundEventAccess)expr, valueKind, diagnostics); + + case BoundKind.UnconvertedSwitchExpression: + case BoundKind.ConvertedSwitchExpression: + return CheckSwitchExpressionValueKind((BoundSwitchExpression)expr, valueKind, diagnostics); } // easy out for a very common RValue case. @@ -1404,8 +1407,11 @@ private void ValidateRefSwitchExpression(SyntaxNode node, ImmutableArray 0) @@ -1582,6 +1588,24 @@ 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; + } + + 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 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/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 70b8956d94823..a2158556ecc77 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7802,4 +7802,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 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 + \ 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 e4e93d932d4f7..e14b126dfd692 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2275,6 +2275,7 @@ internal enum ErrorCode ERR_MismatchedRefEscapeInSwitchExpression = 9300, WRN_MismatchedRefEscapeInSwitchExpression = 9301, + ERR_RequiresRefReturningSwitchExpression = 9302, #endregion diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index f80ed9fa146f9..64121d1625ae7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -699,6 +699,10 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL Debug.Assert(((BoundConditionalOperator)originalLHS).IsRef); break; + case BoundKind.ConvertedSwitchExpression: + Debug.Assert(((BoundSwitchExpression)originalLHS).IsRef); + break; + case BoundKind.AssignmentOperator: Debug.Assert(((BoundAssignmentOperator)originalLHS).IsRef); break; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index a610c5ad424a4..0f31c2a1bc3a4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1722,6 +1722,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}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index e19857e2b0c09..fba3efd18fdf7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1722,6 +1722,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index ee02ffd864acf..d215b5d1dce9e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1722,6 +1722,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 391627ab5f359..47ed26023a90e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1722,6 +1722,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}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 142e3e8b714df..8601ff36ad403 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1722,6 +1722,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 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index bb07cebed033c..e398bd9d0fa3c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1722,6 +1722,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}' にする必要があります diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 50fc56f5728e7..9f389682ee7ad 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1722,6 +1722,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}' 형식이어야 합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 5bbd45bc71e76..9001ac256db18 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1722,6 +1722,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}”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 1a01f02fa6225..467045d269774 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1722,6 +1722,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}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 67af68512efe8..ca693c071bffe 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1722,6 +1722,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}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index f92929c576fa2..ab85e25894478 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1722,6 +1722,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 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index ef765febc9b03..e98b20ee8480e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1722,6 +1722,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}” diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 8a65061ad187b..b496dce870aaf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1722,6 +1722,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}' diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index c8aeceda95edc..1848e7d99b0eb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -5351,41 +5351,52 @@ static void Main() 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 + // 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.GetDirectionField", expressionIL); + verifier.VerifyMethodBody("C.IncrementDirectionField", expressionIL); } #endregion From 4f11334917799dc78f70937ca199a46c00f3f04e Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Thu, 17 Aug 2023 19:06:06 +0300 Subject: [PATCH 06/19] Add ref escape safety tests --- .../Semantics/RefLocalsAndReturnsTests.cs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 1848e7d99b0eb..c020bc5be1da5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -5399,6 +5399,104 @@ .locals init (int& V_0, verifier.VerifyMethodBody("C.IncrementDirectionField", expressionIL); } + [Fact] + public void RefSwitchStatement_RefEscapeWithStackalloc() + { + 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 RefSwitchStatement_RefOverScopedVariables() + { + 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) + ); + } + #endregion } } From b5e5d70eb220b720c085196b31711cde13b8690a Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Fri, 18 Aug 2023 01:25:31 +0300 Subject: [PATCH 07/19] Support ref readonly expressions --- .../Portable/Binder/Binder.ValueChecks.cs | 48 +++++------ .../Binder/SwitchExpressionArmBinder.cs | 2 - .../Portable/Binder/SwitchExpressionBinder.cs | 6 -- .../Semantics/RefLocalsAndReturnsTests.cs | 79 +++++++++++++++++++ 4 files changed, 105 insertions(+), 30 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 254361b6bcd85..5a9b4034fc99f 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 @@ -495,7 +496,7 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind if (expr.Kind == BoundKind.UnconvertedSwitchExpression && valueKind is BindValueKind.RValue or BindValueKind.Assignable) { - return ConvertSwitchExpression((BoundUnconvertedSwitchExpression)expr, expr.Type, null, diagnostics); + expr = ConvertSwitchExpression((BoundUnconvertedSwitchExpression)expr, expr.Type, null, diagnostics); } if (!hasResolutionErrors && CheckValueKind(expr.Syntax, expr, valueKind, checkingReceiver: false, diagnostics: diagnostics) || @@ -558,7 +559,11 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.UnconvertedSwitchExpression: case BoundKind.ConvertedSwitchExpression: - return CheckSwitchExpressionValueKind((BoundSwitchExpression)expr, valueKind, diagnostics); + bool check = CheckSwitchExpressionValueKind((BoundSwitchExpression)expr, valueKind, diagnostics); + if (!check) + return false; + + break; } // easy out for a very common RValue case. @@ -575,6 +580,8 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin return false; } + var errorSpan = node.FullSpan; + switch (expr.Kind) { case BoundKind.NamespaceExpression: @@ -802,28 +809,24 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.ConvertedSwitchExpression: var switchExpression = (BoundSwitchExpression)expr; - if (switchExpression.IsRef) - { - Debug.Assert(switchExpression.SwitchArms.Length > 0, "By-ref switch expressions must always have at least one switch arm"); + Debug.Assert(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; + // 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) - { - check = false; - break; - } - } - if (check) - return true; + check &= CheckValueKind(arm.Value.Syntax, arm.Value, valueKind, checkingReceiver: false, diagnostics: diagnostics); } + if (check) + return true; + + var switchExpressionNode = (CSharp.Syntax.SwitchExpressionSyntax)switchExpression.Syntax; + errorSpan = TextSpan.FromBounds(switchExpressionNode.SpanStart, switchExpressionNode.SwitchKeyword.Span.End); break; @@ -833,7 +836,8 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin } // 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) diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs index c17b3eb06ab85..9462f15da1c1b 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionArmBinder.cs @@ -58,8 +58,6 @@ internal override BoundSwitchExpressionArm BindSwitchExpressionArm(SwitchExpress } BoundExpression armResult = armBinder.BindValue(unwrappedExpression, diagnostics, bindValueKind); - // TODO: Get whether the underlying expression is readonly - var label = new GeneratedLabelSymbol("arm"); 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 487d6bbc3546d..2cd29d17ed59c 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs @@ -155,12 +155,6 @@ static void addNonNullSuccessors(ref TemporaryArray builde when refKind is RefKind.None: refKind = RefKind.Ref; break; - - case RefKind.RefReadOnlyParameter: - case RefKind.RefReadOnly: - // We ignore escape safety here, and only normalize to ref readonly - refKind = RefKind.RefReadOnly; - break; } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index c020bc5be1da5..0842667b04449 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -5497,6 +5497,85 @@ public enum Direction ); } + [Fact] + public void RefSwitchStatement_Readonly01() + { + 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) + ); + } + #endregion } } From 9b5b8ca1f9cfd416c3492753f1891ee7616cc6f0 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Fri, 18 Aug 2023 21:15:48 +0300 Subject: [PATCH 08/19] Fix breaks --- .../Portable/Binder/Binder.ValueChecks.cs | 47 +++++++++++-------- .../CSharp/Portable/Errors/ErrorFacts.cs | 6 ++- .../Semantic/Semantics/InterpolationTests.cs | 6 +-- .../RawInterpolationTests_Handler.cs | 6 +-- .../Test/Syntax/Diagnostics/DiagnosticTest.cs | 1 + 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 5a9b4034fc99f..e50551b2fbc87 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -494,9 +494,15 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind } } - if (expr.Kind == BoundKind.UnconvertedSwitchExpression && valueKind is BindValueKind.RValue or BindValueKind.Assignable) + if (expr.Kind == BoundKind.UnconvertedSwitchExpression && + expr.Type is not null && + valueKind is BindValueKind.RValue or BindValueKind.Assignable) { - expr = ConvertSwitchExpression((BoundUnconvertedSwitchExpression)expr, expr.Type, null, diagnostics); + 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) || @@ -580,7 +586,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin return false; } - var errorSpan = node.FullSpan; + var errorSpan = node.Span; switch (expr.Kind) { @@ -809,26 +815,29 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.ConvertedSwitchExpression: var switchExpression = (BoundSwitchExpression)expr; - Debug.Assert(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) + if (switchExpression.IsRef) { - // Specially handle throw expressions in arms because they are not treated the same elsewhere - if (arm.Value is BoundThrowExpression or BoundConversion { Operand: BoundThrowExpression }) - continue; + Debug.Assert(switchExpression.SwitchArms.Length > 0, "By-ref switch expressions must always have at least one switch arm"); - check &= CheckValueKind(arm.Value.Syntax, arm.Value, valueKind, checkingReceiver: false, diagnostics: diagnostics); - } - if (check) - return true; + // 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; - var switchExpressionNode = (CSharp.Syntax.SwitchExpressionSyntax)switchExpression.Syntax; - errorSpan = TextSpan.FromBounds(switchExpressionNode.SpanStart, switchExpressionNode.SwitchKeyword.Span.End); + check &= CheckValueKind(arm.Value.Syntax, arm.Value, valueKind, checkingReceiver: false, diagnostics: diagnostics); + } + if (check) + return true; - break; + var switchExpressionNode = (CSharp.Syntax.SwitchExpressionSyntax)switchExpression.Syntax; + errorSpan = TextSpan.FromBounds(switchExpressionNode.SpanStart, switchExpressionNode.SwitchKeyword.Span.End); + break; + } + + return true; default: Debug.Assert(expr is not BoundValuePlaceholderBase, $"Placeholder kind {expr.Kind} should be explicitly handled"); diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 6f27f87a94143..14429294f86d3 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -251,7 +251,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_MissingTypeParamTag: case ErrorCode.WRN_InvalidVersionFormat: return 4; - case ErrorCode.WRN_UnreferencedEvent: + case ErrorCode.WRN_UnreferencedEvent: case ErrorCode.WRN_DuplicateUsing: case ErrorCode.WRN_UnreferencedVar: case ErrorCode.WRN_UnreferencedField: @@ -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,9 @@ 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: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement 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/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: From 273ff90d32a56f51cc251169c5559576d4146ed6 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Fri, 18 Aug 2023 21:16:31 +0300 Subject: [PATCH 09/19] Revert whitespace change --- src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 14429294f86d3..d97116e1ffeb1 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -251,7 +251,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_MissingTypeParamTag: case ErrorCode.WRN_InvalidVersionFormat: return 4; - case ErrorCode.WRN_UnreferencedEvent: + case ErrorCode.WRN_UnreferencedEvent: case ErrorCode.WRN_DuplicateUsing: case ErrorCode.WRN_UnreferencedVar: case ErrorCode.WRN_UnreferencedField: From 01881d1a06b7231cc1828c66cb8079c8d40a0cf3 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Fri, 18 Aug 2023 22:27:22 +0300 Subject: [PATCH 10/19] Handle more ref switch error cases --- .../Portable/Binder/Binder.ValueChecks.cs | 13 ++- .../Portable/Binder/SwitchExpressionBinder.cs | 36 ++++++-- .../CSharp/Portable/CSharpResources.resx | 6 ++ .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../CSharp/Portable/Errors/ErrorFacts.cs | 2 + .../Portable/xlf/CSharpResources.cs.xlf | 10 ++ .../Portable/xlf/CSharpResources.de.xlf | 10 ++ .../Portable/xlf/CSharpResources.es.xlf | 10 ++ .../Portable/xlf/CSharpResources.fr.xlf | 10 ++ .../Portable/xlf/CSharpResources.it.xlf | 10 ++ .../Portable/xlf/CSharpResources.ja.xlf | 10 ++ .../Portable/xlf/CSharpResources.ko.xlf | 10 ++ .../Portable/xlf/CSharpResources.pl.xlf | 10 ++ .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 ++ .../Portable/xlf/CSharpResources.ru.xlf | 10 ++ .../Portable/xlf/CSharpResources.tr.xlf | 10 ++ .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 ++ .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 ++ .../Semantics/RefLocalsAndReturnsTests.cs | 91 +++++++++++++++++++ 19 files changed, 269 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index e50551b2fbc87..72eac59321707 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -815,6 +815,9 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin 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"); @@ -831,11 +834,15 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin } if (check) return true; - - var switchExpressionNode = (CSharp.Syntax.SwitchExpressionSyntax)switchExpression.Syntax; - errorSpan = TextSpan.FromBounds(switchExpressionNode.SpanStart, switchExpressionNode.SwitchKeyword.Span.End); break; } + else + { + if (RequiresReferenceToLocation(valueKind)) + { + break; + } + } return true; diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs index 2cd29d17ed59c..ab379aa55bea2 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs @@ -33,6 +33,8 @@ internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSynta 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); @@ -42,6 +44,25 @@ internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSynta 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()); + } + } + } + } + /// /// Build the decision dag, giving an error if some cases are subsumed and a warning if the switch expression is not exhaustive. /// @@ -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); @@ -151,8 +172,7 @@ static void addNonNullSuccessors(ref TemporaryArray builde switch (arm.RefKind) { - case RefKind.Ref - when refKind is RefKind.None: + case RefKind.Ref: refKind = RefKind.Ref; break; } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index a2158556ecc77..85164033ad2b2 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7805,4 +7805,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 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 + \ 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 e14b126dfd692..53e94f97e2d78 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2276,6 +2276,8 @@ internal enum ErrorCode ERR_MismatchedRefEscapeInSwitchExpression = 9300, WRN_MismatchedRefEscapeInSwitchExpression = 9301, ERR_RequiresRefReturningSwitchExpression = 9302, + ERR_MissingRefInSwitchExpressionArm = 9303, + ERR_UnusedSwitchExpressionRef = 9304, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index d97116e1ffeb1..c53cce1164afe 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2403,6 +2403,8 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_MismatchedRefEscapeInSwitchExpression: case ErrorCode.WRN_MismatchedRefEscapeInSwitchExpression: case ErrorCode.ERR_RequiresRefReturningSwitchExpression: + case ErrorCode.ERR_MissingRefInSwitchExpressionArm: + case ErrorCode.ERR_UnusedSwitchExpressionRef: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 0f31c2a1bc3a4..71158b75a9061 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1267,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. @@ -2052,6 +2057,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index fba3efd18fdf7..241c0c34d9798 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1267,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. @@ -2052,6 +2057,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index d215b5d1dce9e..deb665501d072 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1267,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". @@ -2052,6 +2057,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 47ed26023a90e..e044ef9113fee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1267,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'. @@ -2052,6 +2057,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8601ff36ad403..106b5bf6cc4bf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1267,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'. @@ -2052,6 +2057,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index e398bd9d0fa3c..6cc54d259fc13 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1267,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' 属性を設定することはできません。 @@ -2052,6 +2057,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}' 言語バージョンに更新することを検討してください。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 9f389682ee7ad..7226870e4fd45 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1267,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' 특성을 지정할 수 없습니다. @@ -2052,6 +2057,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}'으로 업데이트하는 것이 좋습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 9001ac256db18..e4cb34b65f65b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1267,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”. @@ -2052,6 +2057,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 467045d269774..db148682f4b4a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1267,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'. @@ -2052,6 +2057,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index ca693c071bffe..b021b29ff1849 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1267,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". @@ -2052,6 +2057,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}", чтобы автоматически применить значения по умолчанию к этому полю. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index ab85e25894478..a27060e49b0aa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1267,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. @@ -2052,6 +2057,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. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index e98b20ee8480e..34542df1604ad 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1267,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" 对模块初始值设定项进行特性化。 @@ -2052,6 +2057,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}' 以自动默认字段。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index b496dce870aaf..fe4dbbe2ee84f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1267,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' 將模組初始設定式屬性化。 @@ -2052,6 +2057,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}' 以自動預設欄位。 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 0842667b04449..e2fcbdb3906c1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -5576,6 +5576,97 @@ public enum Direction ); } + [Fact] + public void RefSwitchStatement_MissingRefInArms() + { + 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 RefSwitchStatement_RefInNonRefSwitch() + { + 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 CS1510: A ref or out value must be an assignable variable + // ref int x = ref direction switch + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "direction switch").WithLocation(10, 25) + ); + } + #endregion } } From 893c0a7a8658dbddcd626356ec3975e12ef39de5 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 19 Aug 2023 11:56:04 +0300 Subject: [PATCH 11/19] Add error for discarding switch expression ref --- .../Portable/Binder/Binder.ValueChecks.cs | 7 ++ .../Semantics/RefLocalsAndReturnsTests.cs | 75 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 72eac59321707..ff191c3aa0c27 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -1621,6 +1621,13 @@ private bool CheckSwitchExpressionValueKind(BoundSwitchExpression expression, Bi return false; } return true; + case BindValueKind.RValue: + if (expression.IsRef) + { + Error(diagnostics, ErrorCode.ERR_UnusedSwitchExpressionRef, expression.Syntax); + return false; + } + return true; } return true; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index e2fcbdb3906c1..6df05c7fc4515 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -5576,6 +5576,81 @@ public enum Direction ); } + [Fact] + public void RefSwitchStatement_UnusedRef() + { + 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 RefSwitchStatement_MissingRefInArms() { From 1b8e0784b1d3e039d699d7aa9cbead15f76bfc4d Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 19 Aug 2023 12:34:36 +0300 Subject: [PATCH 12/19] Revert adjustment in AbstractFlowPass --- src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 6586f22a05496..4b99244faca64 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -3212,7 +3212,7 @@ protected void VisitPotentialByRefExpression(BoundExpression operand, bool isByR } else { - VisitRvalue(operand); + Visit(operand); } } From d0b9cf23d5f948ae7530fa115f1150a6aaaa29ac Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 19 Aug 2023 12:49:10 +0300 Subject: [PATCH 13/19] Add test for unsupported ref switch feature --- .../Semantics/RefLocalsAndReturnsTests.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 6df05c7fc4515..6848972ee2315 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -5742,6 +5742,49 @@ public enum Direction ); } + [Fact] + public void RefSwitchStatement_UnavailableFeature() + { + 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 } } From ad878573c766e5b7717eb1df9feae979884103d4 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 19 Aug 2023 12:59:05 +0300 Subject: [PATCH 14/19] Add more helpful error for ref on non-ref switch --- src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs | 5 ++++- src/Compilers/CSharp/Portable/CSharpResources.resx | 3 +++ src/Compilers/CSharp/Portable/Errors/ErrorCode.cs | 1 + src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf | 5 +++++ .../CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf | 5 +++++ .../CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf | 5 +++++ .../Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs | 4 ++-- 17 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index ff191c3aa0c27..1823bf0ff3a27 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -840,7 +840,10 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin { if (RequiresReferenceToLocation(valueKind)) { - break; + // 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; } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 85164033ad2b2..8fba0ebc8a85b 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7811,4 +7811,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 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 53e94f97e2d78..45fb07f4cf14e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2278,6 +2278,7 @@ internal enum ErrorCode ERR_RequiresRefReturningSwitchExpression = 9302, ERR_MissingRefInSwitchExpressionArm = 9303, ERR_UnusedSwitchExpressionRef = 9304, + ERR_RefOnNonRefSwitchExpression = 9305, #endregion diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 71158b75a9061..40a560d0a14d5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 241c0c34d9798..686d23f1fd473 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index deb665501d072..452ec34262e44 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index e044ef9113fee..62e452004767d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 106b5bf6cc4bf..56d24e1b9c9a0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 6cc54d259fc13..79a97d5b1cd72 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 7226870e4fd45..b1a7d075dfb42 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index e4cb34b65f65b..c2a4ac0a05a88 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index db148682f4b4a..8e83880deebbd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index b021b29ff1849..683f8b640f403 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a27060e49b0aa..7dea1d83bd574 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 34542df1604ad..d5ee217c6e58f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index fe4dbbe2ee84f..00dcbe97fa608 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1627,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'. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 6848972ee2315..225542d6f152c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -5736,9 +5736,9 @@ public enum Direction var comp = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview); comp.VerifyEmitDiagnostics( - // (10,25): error CS1510: A ref or out value must be an assignable variable + // (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_RefLvalueExpected, "direction switch").WithLocation(10, 25) + Diagnostic(ErrorCode.ERR_RefOnNonRefSwitchExpression, "direction switch").WithLocation(10, 25) ); } From b0dc6ca02f2c72794a39f9873729cb3687dcf550 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 19 Aug 2023 13:07:20 +0300 Subject: [PATCH 15/19] Minor improvements in test code --- .../Semantics/RefLocalsAndReturnsTests.cs | 69 +++++++++---------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 225542d6f152c..f00949e01f3b3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -4908,14 +4908,14 @@ public void RefLocalInOutVar_01() } } - #region Ref switch statements + #region Ref switch expressions [Theory] [InlineData("North")] [InlineData("South")] [InlineData("East")] [InlineData("West")] - public void RefSwitchStatement_DirectReturnMethod(string direction) + public void RefSwitchExpression_DirectReturnMethod(string direction) { string source = $$""" @@ -4952,7 +4952,7 @@ public ref int GetDirectionField_Target(Direction direction) } } } - + public enum Direction { North, @@ -5081,7 +5081,7 @@ .locals init (int& V_0) [InlineData("South")] [InlineData("East")] [InlineData("West")] - public void RefSwitchStatement_LocalRefVariable(string direction) + public void RefSwitchExpression_LocalRefVariable(string direction) { string source = $$""" @@ -5091,7 +5091,7 @@ public class C public int SouthField; public int EastField; public int WestField; - + public ref int GetDirectionField(Direction direction) { ref int directionVar = ref direction switch @@ -5106,7 +5106,7 @@ public ref int GetDirectionField(Direction direction) return ref directionVar; } } - + public enum Direction { North, @@ -5192,7 +5192,7 @@ .locals init (int& V_0, //directionVar [InlineData("South")] [InlineData("East")] [InlineData("West")] - public void RefSwitchStatement_DirectAssignment(string direction) + public void RefSwitchExpression_DirectAssignment(string direction) { string source = $$""" @@ -5215,7 +5215,7 @@ public void SetDirectionField(Direction direction, int value) } = value; } } - + public enum Direction { North, @@ -5294,7 +5294,7 @@ .locals init (int& V_0) [InlineData("South")] [InlineData("East")] [InlineData("West")] - public void RefSwitchStatement_DirectCompoundAssignment(string direction) + public void RefSwitchExpression_DirectCompoundAssignment(string direction) { string source = $$""" @@ -5317,7 +5317,7 @@ public void IncrementDirectionField(Direction direction, int value) } += value; } } - + public enum Direction { North, @@ -5400,10 +5400,9 @@ .locals init (int& V_0, } [Fact] - public void RefSwitchStatement_RefEscapeWithStackalloc() + public void RefSwitchExpression_RefEscapeWithStackalloc() { - string source = - $$""" + const string source = """ using System; public class C @@ -5413,7 +5412,7 @@ 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], @@ -5444,10 +5443,9 @@ public enum Axis } [Fact] - public void RefSwitchStatement_RefOverScopedVariables() + public void RefSwitchExpression_RefOverScopedVariables() { - string source = - $$""" + const string source = """ public class C { public int NorthField; @@ -5465,7 +5463,7 @@ public unsafe ref int M(Direction direction, ref int input) Direction.West => ref WestField, _ => throw null!, }; - + int outer = 10; input = ref outer; { @@ -5498,10 +5496,9 @@ public enum Direction } [Fact] - public void RefSwitchStatement_Readonly01() + public void RefSwitchExpression_Readonly01() { - string source = - $$""" + const string source = """ public class C { public int NorthField; @@ -5521,7 +5518,7 @@ public unsafe ref int M(Direction direction, in int south, in int west) Direction.West => ref west, _ => throw null!, }; - + ref int xx = ref direction switch { Direction.North => ref NorthField, @@ -5530,7 +5527,7 @@ public unsafe ref int M(Direction direction, in int south, in int west) Direction.West => ref west, _ => throw null!, }; - + ref readonly int y = ref direction switch { Direction.North => ref NorthField, @@ -5577,10 +5574,9 @@ public enum Direction } [Fact] - public void RefSwitchStatement_UnusedRef() + public void RefSwitchExpression_UnusedRef() { - string source = - $$""" + const string source = """ public class C { public int NorthField; @@ -5597,7 +5593,7 @@ public int M(Direction direction) Direction.East => ref EastField, Direction.West => ref WestField, }; - + // Method invocation A(direction switch { @@ -5652,10 +5648,9 @@ public enum Direction } [Fact] - public void RefSwitchStatement_MissingRefInArms() + public void RefSwitchExpression_MissingRefInArms() { - string source = - $$""" + const string source = """ public class C { public int NorthField; @@ -5673,7 +5668,7 @@ public int M(Direction direction) Direction.West => WestField, _ => throw null!, }; - + return x; } } @@ -5699,10 +5694,9 @@ public enum Direction } [Fact] - public void RefSwitchStatement_RefInNonRefSwitch() + public void RefSwitchExpression_RefInNonRefSwitch() { - string source = - $$""" + const string source = """ public class C { public int NorthField; @@ -5720,7 +5714,7 @@ public int M(Direction direction) Direction.West => WestField, _ => throw null!, }; - + return x; } } @@ -5743,10 +5737,9 @@ public enum Direction } [Fact] - public void RefSwitchStatement_UnavailableFeature() + public void RefSwitchExpression_UnavailableFeature() { - string source = - $$""" + const string source = """ class C { public void M(int input) From ea3054639ff31b23b6623f45a05b0531b50171c7 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 19 Aug 2023 23:05:39 +0300 Subject: [PATCH 16/19] Revert breaks --- .../CSharp/Portable/Binder/Binder.ValueChecks.cs | 2 -- .../CSharp/Portable/Binder/Binder_Statements.cs | 4 ++-- .../CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs | 5 ----- .../Portable/FlowAnalysis/AbstractFlowPass_Switch.cs | 12 +++++++++++- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 1823bf0ff3a27..b623bd371ddd8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -3186,7 +3186,6 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres if (switchExpression.IsRef) { - // ref conditional defers to its operands uint maxScope = uint.MinValue; foreach (var arm in switchExpression.SwitchArms) { @@ -3201,7 +3200,6 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return maxScope; } - // otherwise it is an RValue break; case BoundKind.FieldAccess: diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 5629948d43049..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 ConvertIdentityRefExpression(expression, targetType, diagnostics, ref useSiteInfo); + return ConvertIdentityRefExpression(expression, targetType, diagnostics); } } else if (!conversion.IsValid || @@ -1965,7 +1965,7 @@ 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, ref CompoundUseSiteInfo useSiteInfo) + private BoundExpression ConvertIdentityRefExpression(BoundExpression expression, TypeSymbol destination, BindingDiagnosticBag diagnostics) { switch (expression.Kind) { diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 4b99244faca64..c79b9f29b0970 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -3199,11 +3199,6 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node private void VisitConditionalOperand(TLocalState state, BoundExpression operand, bool isByRef) { SetState(state); - VisitPotentialByRefExpression(operand, isByRef); - } - - protected void VisitPotentialByRefExpression(BoundExpression operand, bool isByRef) - { if (isByRef) { VisitLvalue(operand); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs index 1a3a6b5e6ab6c..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); } - VisitPotentialByRefExpression(arm.Value, arm.IsRef); + 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); } From 9b5b75691b7252c6c7d3c4af466cb74e631abb84 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 19 Aug 2023 23:55:33 +0300 Subject: [PATCH 17/19] Add missing error case --- src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index c53cce1164afe..a61300c0bf0d2 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2405,6 +2405,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) 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 From 66ef4c8bbd480497d75e02bf9e93e6a0125a931f Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 20 Aug 2023 09:52:31 +0300 Subject: [PATCH 18/19] Update and fix tests --- .../CSharp/Portable/CSharpResources.resx | 3 ++ .../Emit2/Semantics/PatternMatchingTests3.cs | 32 ++----------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 8fba0ebc8a85b..436279adc256f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7802,6 +7802,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop 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 switch expression in the left side of a compound assignment must return a by-ref expression 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] From 7f51446c25270e8d36e78d79d29a9547dac2646d Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 20 Aug 2023 12:00:40 +0300 Subject: [PATCH 19/19] Update resources --- src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf | 5 +++++ src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf | 5 +++++ .../CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf | 5 +++++ .../CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf | 5 +++++ 13 files changed, 65 insertions(+) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 40a560d0a14d5..22254d1aba87c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2687,6 +2687,11 @@ 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 686d23f1fd473..2754e040f7ad1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2687,6 +2687,11 @@ 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 452ec34262e44..cdf7f4b8370c5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2687,6 +2687,11 @@ 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 62e452004767d..97551229a2012 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2687,6 +2687,11 @@ 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 56d24e1b9c9a0..2d90d175d4ec0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2687,6 +2687,11 @@ 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 79a97d5b1cd72..a5800b6e8f951 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2687,6 +2687,11 @@ 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 b1a7d075dfb42..a5efc85329143 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2687,6 +2687,11 @@ 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 c2a4ac0a05a88..e5a47ba64bb66 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2687,6 +2687,11 @@ 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 8e83880deebbd..afb6fae825cf6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2687,6 +2687,11 @@ 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 683f8b640f403..89000384350fa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2687,6 +2687,11 @@ 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 7dea1d83bd574..5c175ba5e099f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2687,6 +2687,11 @@ 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 d5ee217c6e58f..96d3c14adf325 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2687,6 +2687,11 @@ 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 00dcbe97fa608..0925692e56ca6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2687,6 +2687,11 @@ 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 條件運算子的分支參考具有不相容宣告範圍的變數