From 596402e16d52ee09be9da1a0e39a9a2a264c73d2 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 27 Feb 2026 16:09:57 -0800 Subject: [PATCH 1/6] Fix another RuntimeAsync NRE with hoisted methods As pointed out by @AlekseyTs, `CSharpCompilation.IsRuntimeAsyncEnabledIn` was assuming that only `SourceMethodSymbol`s would be passed in. This isn't true for extension blocks after local rewriting, which indeed caused a crash when a couple of tests were augmented with attribute-level runtime async codegen suppression. To fix, we move the attribute check up to `MethodSymbol` and implement across all of our symbol types. Fixes https://github.com/dotnet/roslyn/issues/82571. --- .../Portable/Compilation/CSharpCompilation.cs | 6 +- .../Lowering/SynthesizedMethodBaseSymbol.cs | 4 +- .../Portable/Symbols/ErrorMethodSymbol.cs | 2 + .../FunctionPointerMethodSymbol.cs | 1 + .../Symbols/Metadata/PE/PEMethodSymbol.cs | 2 + .../CSharp/Portable/Symbols/MethodSymbol.cs | 2 + .../Symbols/ReducedExtensionMethodSymbol.cs | 2 + .../Symbols/SignatureOnlyMethodSymbol.cs | 2 + .../SourceMethodSymbolWithAttributes.cs | 2 +- .../SynthesizedEntryPointSymbol.cs | 2 + .../SynthesizedGlobalMethodSymbol.cs | 2 + .../SynthesizedIntrinsicOperatorSymbol.cs | 2 + .../Synthesized/SynthesizedMethodSymbol.cs | 2 + .../SynthesizedStaticConstructor.cs | 2 + .../Symbols/Wrapped/WrappedMethodSymbol.cs | 2 + .../Emit/CodeGen/CodeGenAsyncSpillTests.cs | 42 +++++---- .../Attributes/AttributeTests_Synthesized.cs | 88 ++++++++++++++----- 17 files changed, 119 insertions(+), 46 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 7a4bb9bf9ce8a..500e4461522b8 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -358,10 +358,10 @@ internal bool IsRuntimeAsyncEnabledIn(Symbol? symbol) Debug.Assert(ReferenceEquals(method.ContainingAssembly, Assembly)); Debug.Assert(method.IsDefinition); - var runtimeAsyncEnabledInMethod = symbol switch + var runtimeAsyncEnabledInMethod = method.IsRuntimeAsyncEnabledInMethod switch { - SourceMethodSymbol { IsRuntimeAsyncEnabledInMethod: ThreeState.True } => true, - SourceMethodSymbol { IsRuntimeAsyncEnabledInMethod: ThreeState.False } => false, + ThreeState.True => true, + ThreeState.False => false, _ => Feature(CodeAnalysis.Feature.RuntimeAsync) == "on" }; diff --git a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs index d13dedd6d70f5..fb3ad279434c5 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs @@ -215,8 +215,8 @@ public sealed override bool IsImplicitlyDeclared } internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => - InheritsBaseMethodAttributes && BaseMethod is SourceMethodSymbol { IsRuntimeAsyncEnabledInMethod: var value } - ? value + InheritsBaseMethodAttributes + ? BaseMethod.IsRuntimeAsyncEnabledInMethod : ThreeState.Unknown; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs index bf9c36d438ec1..55ccf7d5b0c03 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs @@ -178,6 +178,8 @@ public override bool ReturnsVoid public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + public override bool IsVararg { get { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index 4c6cbbdb45816..d447504e89de4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -845,6 +845,7 @@ public override bool IsVararg public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None; public override ImmutableHashSet ReturnNotNullIfParameterNotNull => ImmutableHashSet.Empty; public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false; internal override bool IsMetadataVirtual(IsMetadataVirtualOption option = IsMetadataVirtualOption.None) => false; internal sealed override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index 81f824f813411..80155086f3d08 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -702,6 +702,8 @@ public override FlowAnalysisAnnotations FlowAnalysisAnnotations } } + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal override ImmutableArray NotNullMembers { get diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index 1428c4574fefe..4d4c598509580 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -98,6 +98,8 @@ public virtual bool IsGenericMethod internal abstract bool HasSpecialNameAttribute { get; } + internal abstract ThreeState IsRuntimeAsyncEnabledInMethod { get; } + /// /// If a method is annotated with `[MemberNotNull(...)]` attributes, returns the list of members /// listed in those attributes. diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 5ed29b79ba21c..90698037d3e37 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -503,6 +503,8 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => _reducedFrom.FlowAnalysisAnnotations; + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => _reducedFrom.IsRuntimeAsyncEnabledInMethod; + public override ImmutableArray RefCustomModifiers { get { return _typeMap.SubstituteCustomModifiers(_reducedFrom.RefCustomModifiers); } diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index 1795defaa7f7f..3ca768cfb63e4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -190,6 +190,8 @@ internal override bool IsMetadataFinal internal sealed override int TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => throw ExceptionUtilities.Unreachable(); + #endregion } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 24f29a248f35e..c077c6c7a2320 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -688,7 +688,7 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut } } - internal virtual ThreeState IsRuntimeAsyncEnabledInMethod + internal override ThreeState IsRuntimeAsyncEnabledInMethod => GetDecodedWellKnownAttributeData()?.RuntimeAsyncMethodGenerationSetting ?? ThreeState.Unknown; internal override ImmutableArray NotNullMembers => diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 19516e64b273d..4dfd01a242e3d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -148,6 +148,8 @@ public override bool ReturnsVoid public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + public override MethodKind MethodKind { get { return MethodKind.Ordinary; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs index eeb976077d5e7..aff22d025ce73 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs @@ -218,6 +218,8 @@ public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations get { return FlowAnalysisAnnotations.None; } } + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + public override ImmutableArray RefCustomModifiers { get { return ImmutableArray.Empty; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index c922152f6c21a..02fc06c26790a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -236,6 +236,8 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + public override ImmutableArray TypeArgumentsWithAnnotations { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs index bbfc7ee1980c0..c9a89e70e711f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs @@ -91,6 +91,8 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; + internal override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal override bool IsNullableAnalysisEnabled() => false; internal sealed override bool HasUnscopedRefAttribute => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs index 3fb90e276ae58..10be7b294bd6c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs @@ -146,6 +146,8 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + public override ImmutableArray RefCustomModifiers { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs index db2fb11d4e3c8..095939a4eb328 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs @@ -329,6 +329,8 @@ internal override bool HasRuntimeSpecialName public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => UnderlyingMethod.FlowAnalysisAnnotations; + internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => UnderlyingMethod.IsRuntimeAsyncEnabledInMethod; + internal sealed override ImmutableArray NotNullMembers => UnderlyingMethod.NotNullMembers; internal sealed override ImmutableArray NotNullWhenTrueMembers => UnderlyingMethod.NotNullWhenTrueMembers; diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 6fc12c66a9357..5316807bd823a 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -8514,10 +8514,11 @@ .locals init (S V_0) """); } - [Fact] - public void SpillAssignmentToThisStruct_02() + [Theory] + [CombinatorialData] + public void SpillAssignmentToThisStruct_02(bool suppressRuntimeAsync) { - var source = """ + var source = $$""" using System; using System.Threading.Tasks; struct S : I @@ -8543,6 +8544,7 @@ static class Extensions { extension(T t) where T : I { + {{(suppressRuntimeAsync ? "[System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)]" : "")}} public async Task M() { t.P = 1; @@ -8550,7 +8552,7 @@ public async Task M() } } } - """; + """ + RuntimeAsyncMethodGenerationAttributeDefinition; var expectedOutput = "0"; CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); @@ -8559,24 +8561,32 @@ public async Task M() var comp = CreateRuntimeAsyncCompilation(source, TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: RuntimeAsyncTestHelpers.ExpectedOutput(expectedOutput), verify: Verification.Fails with { - ILVerifyMessage = """ + ILVerifyMessage = suppressRuntimeAsync + ? """ + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + : """ [Main]: Return value missing on the stack. { Offset = 0x1f } [M]: Return value missing on the stack. { Offset = 0xe } """ }); verifier.VerifyDiagnostics(); - verifier.VerifyIL("Extensions.M(this T)", """ - { - // Code size 15 (0xf) - .maxstack 2 - IL_0000: ldarga.s V_0 - IL_0002: ldc.i4.1 - IL_0003: constrained. "T" - IL_0009: callvirt "void I.P.set" - IL_000e: ret - } - """); + if (!suppressRuntimeAsync) + { + + verifier.VerifyIL("Extensions.M(this T)", """ + { + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldc.i4.1 + IL_0003: constrained. "T" + IL_0009: callvirt "void I.P.set" + IL_000e: ret + } + """); + } } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_Synthesized.cs b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_Synthesized.cs index c92d68cf1a4e7..888dd4121642b 100644 --- a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_Synthesized.cs +++ b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_Synthesized.cs @@ -56,6 +56,20 @@ public static IEnumerable FullMatrixTheoryData } } } + + public static IEnumerable RuntimeAsyncSuppressionAndOptimizationLevelTheoryData + { + get + { + foreach (bool suppressRuntimeAsync in new[] { false, true }) + { + foreach (var level in Enum.GetValues(typeof(OptimizationLevel))) + { + yield return new object[] { level, suppressRuntimeAsync }; + } + } + } + } #endregion #region Helpers @@ -1949,16 +1963,17 @@ public void AsyncStateMachineAttribute_RuntimeAsync_TLSEntrypoint(OptimizationLe } [Theory] - [MemberData(nameof(OptimizationLevelTheoryData))] - public void AsyncStateMachineAttribute_RuntimeAsync_ExtensionBlockMember(OptimizationLevel optimizationLevel) + [MemberData(nameof(RuntimeAsyncSuppressionAndOptimizationLevelTheoryData))] + public void AsyncStateMachineAttribute_RuntimeAsync_ExtensionBlockMember(OptimizationLevel optimizationLevel, bool suppressRuntimeAsync) { - string source = """ + string source = $$""" using System.Threading.Tasks; public static class Ex { extension(object o) { + {{(suppressRuntimeAsync ? "[System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)]" : "")}} public async Task F() { await Task.Delay(0); @@ -1970,28 +1985,35 @@ public async Task F() var options = TestOptions.CreateTestOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel) .WithMetadataImportOptions(MetadataImportOptions.All); - var compilation = CreateRuntimeAsyncCompilation(source, options); - var verifier = CompileAndVerify(compilation, verify: Verification.Skipped, symbolValidator: static module => + var compilation = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition], options); + var verifier = CompileAndVerify(compilation, verify: Verification.Skipped, symbolValidator: module => { var type = module.GlobalNamespace.GetMember("Ex"); var asyncImplMethod = type.GetMember("F"); - // When runtime async is enabled, no state machine is generated, - // so there should be no AsyncStateMachineAttribute and no DebuggerStepThroughAttribute - Assert.Empty(asyncImplMethod.GetAttributes()); + if (suppressRuntimeAsync) + { + Assert.Contains(asyncImplMethod.GetAttributes(), static a => a.AttributeClass?.Name == "AsyncStateMachineAttribute"); + } + else + { + // When runtime async is enabled, no state machine is generated, + // so there should be no AsyncStateMachineAttribute and no DebuggerStepThroughAttribute + Assert.Empty(asyncImplMethod.GetAttributes()); + } - var extension = type.GetTypeMembers().Single(); + var extension = type.GetTypeMembers().Single(static typeMember => typeMember.GetMembers("F").Any()); var asyncExtensionSignatureMethod = extension.GetMember("F"); - Assert.Empty(asyncExtensionSignatureMethod.GetAttributes()); + Assert.DoesNotContain(asyncExtensionSignatureMethod.GetAttributes(), static a => a.AttributeClass?.Name == "AsyncStateMachineAttribute"); }); verifier.VerifyDiagnostics(); } [Theory] - [MemberData(nameof(OptimizationLevelTheoryData))] - public void AsyncStateMachineAttribute_RuntimeAsync_ExtensionBlockMember_WithLambda(OptimizationLevel optimizationLevel) + [MemberData(nameof(RuntimeAsyncSuppressionAndOptimizationLevelTheoryData))] + public void AsyncStateMachineAttribute_RuntimeAsync_ExtensionBlockMember_WithLambda(OptimizationLevel optimizationLevel, bool suppressRuntimeAsync) { - string source = """ + string source = $$""" using System.Threading.Tasks; public static class Ex @@ -2000,7 +2022,7 @@ public static class Ex { public async Task F() { - var f = async () => { await Task.Delay(0); }; + var f = {{(suppressRuntimeAsync ? "[System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)] " : "")}}async () => { await Task.Delay(0); }; await f(); } } @@ -2010,23 +2032,30 @@ public async Task F() var options = TestOptions.CreateTestOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel) .WithMetadataImportOptions(MetadataImportOptions.All); - var compilation = CreateRuntimeAsyncCompilation(source, options); - var verifier = CompileAndVerify(compilation, verify: Verification.Skipped, symbolValidator: static module => + var compilation = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition], options); + var verifier = CompileAndVerify(compilation, verify: Verification.Skipped, symbolValidator: module => { var type = module.GlobalNamespace.GetMember("Ex"); var typeMembers = type.GetTypeMembers(); AssertEx.SequenceEqual(["Ex.$C43E2675C7BBF9284AF22FB8A9BF0280.$119AA281C143547563250CAF89B48A76", "Ex.<>c"], typeMembers.ToTestDisplayStrings()); var asyncLambda = typeMembers[1].GetMember("b__1_0"); - Assert.Empty(asyncLambda.GetAttributes()); + if (suppressRuntimeAsync) + { + Assert.Contains(asyncLambda.GetAttributes(), static a => a.AttributeClass?.Name == "AsyncStateMachineAttribute"); + } + else + { + Assert.Empty(asyncLambda.GetAttributes()); + } }); verifier.VerifyDiagnostics(); } [Theory] - [MemberData(nameof(OptimizationLevelTheoryData))] - public void AsyncStateMachineAttribute_RuntimeAsync_ExtensionBlockMember_WithLocalFunction(OptimizationLevel optimizationLevel) + [MemberData(nameof(RuntimeAsyncSuppressionAndOptimizationLevelTheoryData))] + public void AsyncStateMachineAttribute_RuntimeAsync_ExtensionBlockMember_WithLocalFunction(OptimizationLevel optimizationLevel, bool suppressRuntimeAsync) { - string source = """ + string source = $$""" using System.Threading.Tasks; public static class Ex @@ -2036,6 +2065,7 @@ public static class Ex public async Task F() { await LocalAsync(); + {{(suppressRuntimeAsync ? "[System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)]" : "")}} async Task LocalAsync() { await Task.Delay(0); } } } @@ -2045,13 +2075,23 @@ public async Task F() var options = TestOptions.CreateTestOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel) .WithMetadataImportOptions(MetadataImportOptions.All); - var compilation = CreateRuntimeAsyncCompilation(source, options); - var verifier = CompileAndVerify(compilation, verify: Verification.Skipped, symbolValidator: static module => + var compilation = suppressRuntimeAsync + ? CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition], options) + : CreateRuntimeAsyncCompilation(source, options); + var verifier = CompileAndVerify(compilation, verify: Verification.Skipped, symbolValidator: module => { var type = module.GlobalNamespace.GetMember("Ex"); - Assert.Single(type.GetTypeMembers()); + Assert.Equal(suppressRuntimeAsync ? 2 : 1, type.GetTypeMembers().Length); var localFunction = type.GetMember("g__LocalAsync|1_0"); - AssertEx.SequenceEqual(["System.Runtime.CompilerServices.CompilerGeneratedAttribute..ctor()"], localFunction.GetAttributes().SelectAsArray(a => a.AttributeConstructor.ToTestDisplayString())); + Assert.Contains(localFunction.GetAttributes(), static a => a.AttributeClass?.Name == "CompilerGeneratedAttribute"); + if (suppressRuntimeAsync) + { + Assert.Contains(localFunction.GetAttributes(), static a => a.AttributeClass?.Name == "AsyncStateMachineAttribute"); + } + else + { + Assert.DoesNotContain(localFunction.GetAttributes(), static a => a.AttributeClass?.Name == "AsyncStateMachineAttribute"); + } }); verifier.VerifyDiagnostics(); } From 0bc60d625f03413b5d30658311331006a76fa70d Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 4 Mar 2026 09:59:00 -0800 Subject: [PATCH 2/6] Implement in IDE layer. --- .../Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs | 4 ++++ .../ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index 9ad9e2400449b..c1e4d1c823e6d 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -189,6 +189,7 @@ internal EEMethodSymbol( localsMap.Free(); _generateMethodBody = generateMethodBody; + IsRuntimeAsyncEnabledInMethod = sourceMethod.IsRuntimeAsyncEnabledInMethod; ImmutableArray remapLocalsForBinding( ImmutableArray sourceLocalsForBinding, @@ -777,6 +778,9 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); + // https://github.com/dotnet/roslyn/issues/79793 - test ENC + internal override ThreeState IsRuntimeAsyncEnabledInMethod { get; } + internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument) { builderArgument = null; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs index 3ffbf293a6a61..1a38a207e0100 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs @@ -281,6 +281,8 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); + internal override ThreeState IsRuntimeAsyncEnabledInMethod => throw ExceptionUtilities.Unreachable(); + internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument) { builderArgument = null; From d7168ac959e22e6adc80cbe77d15e4c49355c902 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Tue, 10 Mar 2026 11:48:04 -0700 Subject: [PATCH 3/6] Address feedback, rename property for clarity. --- .../Portable/Compilation/CSharpCompilation.cs | 3 +- .../Lowering/SynthesizedMethodBaseSymbol.cs | 4 +-- .../Portable/Symbols/ErrorMethodSymbol.cs | 2 +- .../FunctionPointerMethodSymbol.cs | 2 +- .../Symbols/Metadata/PE/PEMethodSymbol.cs | 9 +++++- .../CSharp/Portable/Symbols/MethodSymbol.cs | 7 ++++- .../Symbols/ReducedExtensionMethodSymbol.cs | 2 +- .../Symbols/SignatureOnlyMethodSymbol.cs | 2 +- .../SourceMethodSymbolWithAttributes.cs | 2 +- .../SynthesizedEntryPointSymbol.cs | 2 +- .../SynthesizedGlobalMethodSymbol.cs | 2 +- .../SynthesizedIntrinsicOperatorSymbol.cs | 2 +- .../Synthesized/SynthesizedMethodSymbol.cs | 2 +- .../SynthesizedStaticConstructor.cs | 2 +- .../Symbols/Wrapped/WrappedMethodSymbol.cs | 2 +- .../Emit/CodeGen/CodeGenAsyncSpillTests.cs | 30 +++++++++---------- .../Symbols/EEMethodSymbol.cs | 4 +-- .../Symbols/PlaceholderMethodSymbol.cs | 2 +- 18 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 500e4461522b8..1e13ba286f6d6 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -357,8 +357,9 @@ internal bool IsRuntimeAsyncEnabledIn(Symbol? symbol) Debug.Assert(ReferenceEquals(method.ContainingAssembly, Assembly)); Debug.Assert(method.IsDefinition); + Debug.Assert(method is not Symbols.Metadata.PE.PEMethodSymbol); - var runtimeAsyncEnabledInMethod = method.IsRuntimeAsyncEnabledInMethod switch + var runtimeAsyncEnabledInMethod = method.IsRuntimeAsyncExplicitlyControlledInMethod switch { ThreeState.True => true, ThreeState.False => false, diff --git a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs index fb3ad279434c5..374f7cae1279a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs @@ -214,9 +214,9 @@ public sealed override bool IsImplicitlyDeclared get { return true; } } - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => InheritsBaseMethodAttributes - ? BaseMethod.IsRuntimeAsyncEnabledInMethod + ? BaseMethod.IsRuntimeAsyncExplicitlyControlledInMethod : ThreeState.Unknown; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs index 55ccf7d5b0c03..6b8a583cc499d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs @@ -178,7 +178,7 @@ public override bool ReturnsVoid public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; public override bool IsVararg { diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index d447504e89de4..5116b7fefb533 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -845,7 +845,7 @@ public override bool IsVararg public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None; public override ImmutableHashSet ReturnNotNullIfParameterNotNull => ImmutableHashSet.Empty; public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => throw ExceptionUtilities.Unreachable(); internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false; internal override bool IsMetadataVirtual(IsMetadataVirtualOption option = IsMetadataVirtualOption.None) => false; internal sealed override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index 80155086f3d08..1f9bd7ded1c14 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -702,7 +702,14 @@ public override FlowAnalysisAnnotations FlowAnalysisAnnotations } } - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod + { + get + { + Debug.Fail("Not expecting to get here; if we end up here through ENC, add tests to verify"); + return ThreeState.Unknown; + } + } internal override ImmutableArray NotNullMembers { diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index 4d4c598509580..7b114ffc7b05e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -98,7 +98,12 @@ public virtual bool IsGenericMethod internal abstract bool HasSpecialNameAttribute { get; } - internal abstract ThreeState IsRuntimeAsyncEnabledInMethod { get; } + /// + /// Returns the method-level runtime async setting from + /// RuntimeAsyncMethodGenerationAttribute, or + /// if no setting was specified. + /// + internal abstract ThreeState IsRuntimeAsyncExplicitlyControlledInMethod { get; } /// /// If a method is annotated with `[MemberNotNull(...)]` attributes, returns the list of members diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 90698037d3e37..38f109c2a7ff9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -503,7 +503,7 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => _reducedFrom.FlowAnalysisAnnotations; - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => _reducedFrom.IsRuntimeAsyncEnabledInMethod; + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => throw ExceptionUtilities.Unreachable(); public override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index 3ca768cfb63e4..4308c3cbdcb47 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -190,7 +190,7 @@ internal override bool IsMetadataFinal internal sealed override int TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => throw ExceptionUtilities.Unreachable(); + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => throw ExceptionUtilities.Unreachable(); #endregion } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index c077c6c7a2320..5c188d366e733 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -688,7 +688,7 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut } } - internal override ThreeState IsRuntimeAsyncEnabledInMethod + internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => GetDecodedWellKnownAttributeData()?.RuntimeAsyncMethodGenerationSetting ?? ThreeState.Unknown; internal override ImmutableArray NotNullMembers => diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 4dfd01a242e3d..c0f86e5d99279 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -148,7 +148,7 @@ public override bool ReturnsVoid public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; public override MethodKind MethodKind { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs index aff22d025ce73..49408b187c3cb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs @@ -218,7 +218,7 @@ public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations get { return FlowAnalysisAnnotations.None; } } - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; public override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index 02fc06c26790a..7e8be52ec1711 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -236,7 +236,7 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; public override ImmutableArray TypeArgumentsWithAnnotations { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs index c9a89e70e711f..70f4e2afb18b3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs @@ -91,7 +91,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; internal override bool IsNullableAnalysisEnabled() => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs index 10be7b294bd6c..4b37a676de803 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs @@ -146,7 +146,7 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => ThreeState.Unknown; + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; public override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs index 095939a4eb328..21013e71c7233 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs @@ -329,7 +329,7 @@ internal override bool HasRuntimeSpecialName public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => UnderlyingMethod.FlowAnalysisAnnotations; - internal sealed override ThreeState IsRuntimeAsyncEnabledInMethod => UnderlyingMethod.IsRuntimeAsyncEnabledInMethod; + internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => UnderlyingMethod.IsRuntimeAsyncExplicitlyControlledInMethod; internal sealed override ImmutableArray NotNullMembers => UnderlyingMethod.NotNullMembers; diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 5316807bd823a..ea05fd0818733 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -8516,7 +8516,7 @@ .locals init (S V_0) [Theory] [CombinatorialData] - public void SpillAssignmentToThisStruct_02(bool suppressRuntimeAsync) + public void SpillAssignmentToThisStruct_02(bool disableRuntimeAsync) { var source = $$""" using System; @@ -8544,7 +8544,7 @@ static class Extensions { extension(T t) where T : I { - {{(suppressRuntimeAsync ? "[System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)]" : "")}} + {{(disableRuntimeAsync ? "[System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)]" : "")}} public async Task M() { t.P = 1; @@ -8552,16 +8552,16 @@ public async Task M() } } } - """ + RuntimeAsyncMethodGenerationAttributeDefinition; + """; var expectedOutput = "0"; - CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); - CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + CompileAndVerify([source, RuntimeAsyncMethodGenerationAttributeDefinition], expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify([source, RuntimeAsyncMethodGenerationAttributeDefinition], expectedOutput: expectedOutput, options: TestOptions.DebugExe); - var comp = CreateRuntimeAsyncCompilation(source, TestOptions.ReleaseExe); + var comp = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition], TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: RuntimeAsyncTestHelpers.ExpectedOutput(expectedOutput), verify: Verification.Fails with { - ILVerifyMessage = suppressRuntimeAsync + ILVerifyMessage = disableRuntimeAsync ? """ [Main]: Return value missing on the stack. { Offset = 0x1f } """ @@ -8572,18 +8572,18 @@ public async Task M() }); verifier.VerifyDiagnostics(); - if (!suppressRuntimeAsync) + if (!disableRuntimeAsync) { verifier.VerifyIL("Extensions.M(this T)", """ { - // Code size 15 (0xf) - .maxstack 2 - IL_0000: ldarga.s V_0 - IL_0002: ldc.i4.1 - IL_0003: constrained. "T" - IL_0009: callvirt "void I.P.set" - IL_000e: ret + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldc.i4.1 + IL_0003: constrained. "T" + IL_0009: callvirt "void I.P.set" + IL_000e: ret } """); } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index c1e4d1c823e6d..45f511c546b7a 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -189,7 +189,7 @@ internal EEMethodSymbol( localsMap.Free(); _generateMethodBody = generateMethodBody; - IsRuntimeAsyncEnabledInMethod = sourceMethod.IsRuntimeAsyncEnabledInMethod; + IsRuntimeAsyncExplicitlyControlledInMethod = sourceMethod.IsRuntimeAsyncExplicitlyControlledInMethod; ImmutableArray remapLocalsForBinding( ImmutableArray sourceLocalsForBinding, @@ -779,7 +779,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); // https://github.com/dotnet/roslyn/issues/79793 - test ENC - internal override ThreeState IsRuntimeAsyncEnabledInMethod { get; } + internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod { get; } internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument) { diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs index 1a38a207e0100..e7d0109360c72 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs @@ -281,7 +281,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); - internal override ThreeState IsRuntimeAsyncEnabledInMethod => throw ExceptionUtilities.Unreachable(); + internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument) { From 397a5497e965bd15d7d5f3f5e2c39f044b142e61 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Wed, 11 Mar 2026 09:10:33 -0700 Subject: [PATCH 4/6] Rename property, clarify EEMethodSymbol implementation. --- .../CSharp/Portable/Compilation/CSharpCompilation.cs | 2 +- .../Portable/Lowering/SynthesizedMethodBaseSymbol.cs | 4 ++-- src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs | 2 +- .../FunctionPointers/FunctionPointerMethodSymbol.cs | 2 +- .../CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs | 2 +- src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs | 2 +- .../Portable/Symbols/ReducedExtensionMethodSymbol.cs | 2 +- .../CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs | 2 +- .../Symbols/Source/SourceMethodSymbolWithAttributes.cs | 2 +- .../Symbols/Synthesized/SynthesizedEntryPointSymbol.cs | 2 +- .../Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs | 2 +- .../Synthesized/SynthesizedIntrinsicOperatorSymbol.cs | 2 +- .../Symbols/Synthesized/SynthesizedMethodSymbol.cs | 2 +- .../Symbols/Synthesized/SynthesizedStaticConstructor.cs | 2 +- .../CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs | 2 +- .../Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs | 7 +++---- .../ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs | 2 +- 17 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 1e13ba286f6d6..e588c5d3ac09d 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -359,7 +359,7 @@ internal bool IsRuntimeAsyncEnabledIn(Symbol? symbol) Debug.Assert(method.IsDefinition); Debug.Assert(method is not Symbols.Metadata.PE.PEMethodSymbol); - var runtimeAsyncEnabledInMethod = method.IsRuntimeAsyncExplicitlyControlledInMethod switch + var runtimeAsyncEnabledInMethod = method.RuntimeAsyncMethodGenerationAttributeSetting switch { ThreeState.True => true, ThreeState.False => false, diff --git a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs index 374f7cae1279a..2a5fab97aee21 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs @@ -214,9 +214,9 @@ public sealed override bool IsImplicitlyDeclared get { return true; } } - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => InheritsBaseMethodAttributes - ? BaseMethod.IsRuntimeAsyncExplicitlyControlledInMethod + ? BaseMethod.RuntimeAsyncMethodGenerationAttributeSetting : ThreeState.Unknown; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs index 6b8a583cc499d..7a9d7fad82363 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs @@ -178,7 +178,7 @@ public override bool ReturnsVoid public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => ThreeState.Unknown; public override bool IsVararg { diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index 5116b7fefb533..38310a8aacd06 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -845,7 +845,7 @@ public override bool IsVararg public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None; public override ImmutableHashSet ReturnNotNullIfParameterNotNull => ImmutableHashSet.Empty; public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => throw ExceptionUtilities.Unreachable(); + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false; internal override bool IsMetadataVirtual(IsMetadataVirtualOption option = IsMetadataVirtualOption.None) => false; internal sealed override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index 1f9bd7ded1c14..e5c89e4254491 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -702,7 +702,7 @@ public override FlowAnalysisAnnotations FlowAnalysisAnnotations } } - internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod + internal override ThreeState RuntimeAsyncMethodGenerationAttributeSetting { get { diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index 7b114ffc7b05e..bfa116808aa81 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -103,7 +103,7 @@ public virtual bool IsGenericMethod /// RuntimeAsyncMethodGenerationAttribute, or /// if no setting was specified. /// - internal abstract ThreeState IsRuntimeAsyncExplicitlyControlledInMethod { get; } + internal abstract ThreeState RuntimeAsyncMethodGenerationAttributeSetting { get; } /// /// If a method is annotated with `[MemberNotNull(...)]` attributes, returns the list of members diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 38f109c2a7ff9..323d2d0f43098 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -503,7 +503,7 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => _reducedFrom.FlowAnalysisAnnotations; - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => throw ExceptionUtilities.Unreachable(); + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); public override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index 4308c3cbdcb47..74ba90670dda3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -190,7 +190,7 @@ internal override bool IsMetadataFinal internal sealed override int TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => throw ExceptionUtilities.Unreachable(); + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); #endregion } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 5c188d366e733..14feba964135a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -688,7 +688,7 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut } } - internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod + internal override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => GetDecodedWellKnownAttributeData()?.RuntimeAsyncMethodGenerationSetting ?? ThreeState.Unknown; internal override ImmutableArray NotNullMembers => diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index c0f86e5d99279..a049f61eafe1f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -148,7 +148,7 @@ public override bool ReturnsVoid public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => ThreeState.Unknown; public override MethodKind MethodKind { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs index 49408b187c3cb..de6cd25b135a6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs @@ -218,7 +218,7 @@ public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations get { return FlowAnalysisAnnotations.None; } } - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => ThreeState.Unknown; public override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index 7e8be52ec1711..d79ac70c608f7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -236,7 +236,7 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => ThreeState.Unknown; public override ImmutableArray TypeArgumentsWithAnnotations { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs index 70f4e2afb18b3..aa192a3aee479 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs @@ -91,7 +91,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; + internal override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => ThreeState.Unknown; internal override bool IsNullableAnalysisEnabled() => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs index 4b37a676de803..441b94a1100dc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs @@ -146,7 +146,7 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => ThreeState.Unknown; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => ThreeState.Unknown; public override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs index 21013e71c7233..3fdc9bb279e31 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs @@ -329,7 +329,7 @@ internal override bool HasRuntimeSpecialName public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => UnderlyingMethod.FlowAnalysisAnnotations; - internal sealed override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => UnderlyingMethod.IsRuntimeAsyncExplicitlyControlledInMethod; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => UnderlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; internal sealed override ImmutableArray NotNullMembers => UnderlyingMethod.NotNullMembers; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index 45f511c546b7a..15efd0c2bada7 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -91,6 +91,7 @@ internal EEMethodSymbol( Debug.Assert(sourceMethod.IsDefinition); Debug.Assert(TypeSymbol.Equals((TypeSymbol)sourceMethod.ContainingSymbol, container.SubstitutedSourceType.OriginalDefinition, TypeCompareKind.ConsiderEverything2)); Debug.Assert(sourceLocals.All(l => l.ContainingSymbol == sourceMethod)); + Debug.Assert(!sourceMethod.IsAsync); _container = container; _name = name; @@ -189,7 +190,6 @@ internal EEMethodSymbol( localsMap.Free(); _generateMethodBody = generateMethodBody; - IsRuntimeAsyncExplicitlyControlledInMethod = sourceMethod.IsRuntimeAsyncExplicitlyControlledInMethod; ImmutableArray remapLocalsForBinding( ImmutableArray sourceLocalsForBinding, @@ -326,6 +326,8 @@ public override bool IsAsync get { return false; } } + internal override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); + public override TypeWithAnnotations ReturnTypeWithAnnotations { get @@ -778,9 +780,6 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); - // https://github.com/dotnet/roslyn/issues/79793 - test ENC - internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod { get; } - internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument) { builderArgument = null; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs index e7d0109360c72..5c96bc7a35fc7 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs @@ -281,7 +281,7 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable(); - internal override ThreeState IsRuntimeAsyncExplicitlyControlledInMethod => throw ExceptionUtilities.Unreachable(); + internal override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgument) { From e4b80c9ada0153a609d8091f4cec30b4c30bf7b2 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Thu, 12 Mar 2026 10:02:15 -0700 Subject: [PATCH 5/6] Move IsAsync and RuntimeAsyncMethodGenerationAttributeSetting down. --- .../Symbols/Extensions/RewrittenMethodSymbol.cs | 4 ++++ .../CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs | 4 ++++ .../Symbols/Retargeting/RetargetingMethodSymbol.cs | 4 ++++ .../CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs | 4 ++++ ...ynthesizedCollectionBuilderProjectedMethodSymbol.cs | 3 ++- .../Portable/Symbols/Wrapped/WrappedMethodSymbol.cs | 10 ---------- .../Symbols/EECompilationContextMethod.cs | 4 ++++ 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenMethodSymbol.cs index 504e348e8514b..be787c0bb645c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenMethodSymbol.cs @@ -64,6 +64,10 @@ internal override bool IsIterator get { return _originalMethod.IsIterator; } } + public sealed override bool IsAsync => _originalMethod.IsAsync; + + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => _originalMethod.RuntimeAsyncMethodGenerationAttributeSetting; + internal sealed override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) { return _originalMethod.CalculateLocalSyntaxOffset(localPosition, localTree); diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index 5931850c42ffa..f538ebfb52ca8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -360,6 +360,10 @@ internal NativeIntegerMethodSymbol(NativeIntegerTypeSymbol container, MethodSymb public override ImmutableArray TypeParameters => ImmutableArray.Empty; + public override bool IsAsync => UnderlyingMethod.IsAsync; + + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => UnderlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; + public override ImmutableArray Parameters { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs index 2c8d3bca40777..6def5020e7639 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs @@ -126,6 +126,10 @@ public override ImmutableArray TypeArgumentsWithAnnotations } } + public override bool IsAsync => _underlyingMethod.IsAsync; + + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => _underlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; + public override TypeWithAnnotations ReturnTypeWithAnnotations { get diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs index f75b0c8e57661..40cd3d75e552a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs @@ -171,6 +171,10 @@ public override TypeSymbol ReceiverType } } + public override bool IsAsync => OriginalDefinition.IsAsync; + + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => OriginalDefinition.RuntimeAsyncMethodGenerationAttributeSetting; + public override TypeWithAnnotations GetTypeInferredDuringReduction(TypeParameterSymbol reducedFromTypeParameter) { // This will throw if API shouldn't be supported or there is a problem with the argument. diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedCollectionBuilderProjectedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedCollectionBuilderProjectedMethodSymbol.cs index d0557a9f32e91..47ff544d2818a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedCollectionBuilderProjectedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedCollectionBuilderProjectedMethodSymbol.cs @@ -43,8 +43,10 @@ public override ImmutableArray GetAttributes() => this.UnderlyingMethod.GetAttributes(); public override Symbol ContainingSymbol => this.UnderlyingMethod.ContainingSymbol; + public override bool IsAsync => this.UnderlyingMethod.IsAsync; public override ImmutableArray RefCustomModifiers => this.UnderlyingMethod.RefCustomModifiers; public override TypeWithAnnotations ReturnTypeWithAnnotations => this.UnderlyingMethod.ReturnTypeWithAnnotations; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => this.UnderlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; /// /// The projection method itself is intentionally not obsolete. We don't want to report obsoletion errors when @@ -139,4 +141,3 @@ private sealed class SynthesizedCollectionBuilderProjectedParameterSymbol( internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) => throw ExceptionUtilities.Unreachable(); } } - diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs index 3fdc9bb279e31..3409dd6f15748 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs @@ -140,14 +140,6 @@ public override bool IsVirtual } } - public override bool IsAsync - { - get - { - return UnderlyingMethod.IsAsync; - } - } - public override bool IsOverride { get @@ -329,8 +321,6 @@ internal override bool HasRuntimeSpecialName public sealed override FlowAnalysisAnnotations FlowAnalysisAnnotations => UnderlyingMethod.FlowAnalysisAnnotations; - internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => UnderlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; - internal sealed override ImmutableArray NotNullMembers => UnderlyingMethod.NotNullMembers; internal sealed override ImmutableArray NotNullWhenTrueMembers => UnderlyingMethod.NotNullWhenTrueMembers; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EECompilationContextMethod.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EECompilationContextMethod.cs index 6844b466db7c5..e41b0bee41571 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EECompilationContextMethod.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EECompilationContextMethod.cs @@ -60,6 +60,10 @@ public override ImmutableArray Parameters get { return _parameters; } } + public override bool IsAsync => _underlyingMethod.IsAsync; + + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => _underlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; + public override TypeWithAnnotations ReturnTypeWithAnnotations => _underlyingMethod.ReturnTypeWithAnnotations; public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty; From 9c61c47279cf8bbfcfa55269bd6886edab232900 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Fri, 13 Mar 2026 11:00:59 -0700 Subject: [PATCH 6/6] Mark a number of implementations as unreachable. --- .../CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs | 2 +- .../Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs | 2 +- .../CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs | 2 +- .../SynthesizedCollectionBuilderProjectedMethodSymbol.cs | 2 +- .../ExpressionCompiler/Symbols/EECompilationContextMethod.cs | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index f538ebfb52ca8..4c2656cfa8a53 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -362,7 +362,7 @@ internal NativeIntegerMethodSymbol(NativeIntegerTypeSymbol container, MethodSymb public override bool IsAsync => UnderlyingMethod.IsAsync; - internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => UnderlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); public override ImmutableArray Parameters { diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs index 6def5020e7639..2a68f3ffbf9be 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs @@ -128,7 +128,7 @@ public override ImmutableArray TypeArgumentsWithAnnotations public override bool IsAsync => _underlyingMethod.IsAsync; - internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => _underlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); public override TypeWithAnnotations ReturnTypeWithAnnotations { diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs index 40cd3d75e552a..7e63ff81d0251 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs @@ -173,7 +173,7 @@ public override TypeSymbol ReceiverType public override bool IsAsync => OriginalDefinition.IsAsync; - internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => OriginalDefinition.RuntimeAsyncMethodGenerationAttributeSetting; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); public override TypeWithAnnotations GetTypeInferredDuringReduction(TypeParameterSymbol reducedFromTypeParameter) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedCollectionBuilderProjectedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedCollectionBuilderProjectedMethodSymbol.cs index 47ff544d2818a..5e525e9740f88 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedCollectionBuilderProjectedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedCollectionBuilderProjectedMethodSymbol.cs @@ -46,7 +46,7 @@ public override ImmutableArray GetAttributes() public override bool IsAsync => this.UnderlyingMethod.IsAsync; public override ImmutableArray RefCustomModifiers => this.UnderlyingMethod.RefCustomModifiers; public override TypeWithAnnotations ReturnTypeWithAnnotations => this.UnderlyingMethod.ReturnTypeWithAnnotations; - internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => this.UnderlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); /// /// The projection method itself is intentionally not obsolete. We don't want to report obsoletion errors when diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EECompilationContextMethod.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EECompilationContextMethod.cs index e41b0bee41571..79c0cb22dd2bd 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EECompilationContextMethod.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EECompilationContextMethod.cs @@ -60,9 +60,9 @@ public override ImmutableArray Parameters get { return _parameters; } } - public override bool IsAsync => _underlyingMethod.IsAsync; + public override bool IsAsync => false; - internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => _underlyingMethod.RuntimeAsyncMethodGenerationAttributeSetting; + internal sealed override ThreeState RuntimeAsyncMethodGenerationAttributeSetting => throw ExceptionUtilities.Unreachable(); public override TypeWithAnnotations ReturnTypeWithAnnotations => _underlyingMethod.ReturnTypeWithAnnotations;