|
// 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.
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
#nullable disable
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class RefStructInterfacesTests : CSharpTestBase
{
private static readonly TargetFramework s_targetFrameworkSupportingByRefLikeGenerics = TargetFramework.Net90;
[Theory]
[CombinatorialData]
public void UnscopedRefInInterface_Method_01(bool isVirtual)
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
" + (isVirtual ? "virtual " : "") + @" ref int M()" + (isVirtual ? " => throw null" : "") + @";
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr || !isVirtual ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
Assert.True(m.GlobalNamespace.GetMember<MethodSymbol>("I.M").HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (6,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 6)
);
}
[Fact]
public void UnscopedRefInInterface_Method_02()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
ref int M()
{
throw null;
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
Assert.True(m.GlobalNamespace.GetMember<MethodSymbol>("I.M").HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (6,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 6)
);
}
[Fact]
public void UnscopedRefInInterface_Method_03()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
sealed ref int M()
{
throw null;
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (6,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(6, 6)
);
Assert.False(comp.GetMember<MethodSymbol>("I.M").HasUnscopedRefAttribute);
}
[Fact]
public void UnscopedRefInInterface_Method_04()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
abstract static ref int M();
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (6,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(6, 6)
);
Assert.False(comp.GetMember<MethodSymbol>("I.M").HasUnscopedRefAttribute);
}
[Theory]
[CombinatorialData]
public void UnscopedRefInInterface_Property_01(bool isVirtual)
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
" + (isVirtual ? "virtual " : "") + @" ref int P { get" + (isVirtual ? " => throw null" : "") + @"; }
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr || !isVirtual ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("I.P");
Assert.True(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (6,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 6)
);
}
[Fact]
public void UnscopedRefInInterface_Property_02()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
ref int P => throw null;
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("I.P");
Assert.True(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (6,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 6)
);
}
[Fact]
public void UnscopedRefInInterface_Property_03()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
sealed ref int P => throw null;
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (6,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(6, 6)
);
PropertySymbol propertySymbol = comp.GetMember<PropertySymbol>("I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
[Fact]
public void UnscopedRefInInterface_Property_04()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
abstract static ref int P { get; }
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (6,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(6, 6)
);
PropertySymbol propertySymbol = comp.GetMember<PropertySymbol>("I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
[Fact]
public void UnscopedRefInInterface_Property_05()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
ref int P
{
[UnscopedRef]
get;
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.True(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (8,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(8, 10)
);
}
[Fact]
public void UnscopedRefInInterface_Property_06()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
ref int P
{
[UnscopedRef]
get
{
throw null;
}
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.True(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (8,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(8, 10)
);
}
[Fact]
public void UnscopedRefInInterface_Property_07()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
sealed ref int P
{
[UnscopedRef]
get
{
throw null;
}
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (8,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(8, 10)
);
PropertySymbol propertySymbol = comp.GetMember<PropertySymbol>("I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
[Fact]
public void UnscopedRefInInterface_Property_08()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
abstract static ref int P
{
[UnscopedRef]
get;
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (8,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(8, 10)
);
PropertySymbol propertySymbol = comp.GetMember<PropertySymbol>("I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
[Theory]
[CombinatorialData]
public void UnscopedRefInInterface_Indexer_01(bool isVirtual)
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
" + (isVirtual ? "virtual " : "") + @" ref int this[int i] { get" + (isVirtual ? " => throw null" : "") + @"; }
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr || !isVirtual ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.True(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (6,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 6)
);
}
[Fact]
public void UnscopedRefInInterface_Indexer_02()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
ref int this[int i] => throw null;
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.True(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (6,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 6)
);
}
[Fact]
public void UnscopedRefInInterface_Indexer_03()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
sealed ref int this[int i] => throw null;
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (6,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(6, 6)
);
PropertySymbol propertySymbol = comp.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
[Fact]
public void UnscopedRefInInterface_Indexer_04()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
abstract static ref int this[int i] { get; }
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (7,29): error CS0106: The modifier 'static' is not valid for this item
// abstract static ref int this[int i] { get; }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(7, 29)
);
PropertySymbol propertySymbol = comp.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.IsStatic);
Assert.True(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
[Fact]
public void UnscopedRefInInterface_Indexer_05()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
ref int this[int i]
{
[UnscopedRef]
get;
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.True(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (8,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(8, 10)
);
}
[Fact]
public void UnscopedRefInInterface_Indexer_06()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
ref int this[int i]
{
[UnscopedRef]
get
{
throw null;
}
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.True(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (8,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(8, 10)
);
}
[Fact]
public void UnscopedRefInInterface_Indexer_07()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
sealed ref int this[int i]
{
[UnscopedRef]
get
{
throw null;
}
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (8,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(8, 10)
);
PropertySymbol propertySymbol = comp.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
[Fact]
public void UnscopedRefInInterface_Indexer_08()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
abstract static ref int this[int i]
{
[UnscopedRef]
get;
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (6,29): error CS0106: The modifier 'static' is not valid for this item
// abstract static ref int this[int i]
Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(6, 29)
);
PropertySymbol propertySymbol = comp.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.IsStatic);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.True(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
[Fact]
public void UnscopedRefInImplementation_Method_01()
{
var src1 = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
[UnscopedRef]
ref int M();
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80);
MetadataReference[] comp1Refs = [comp1.EmitToImageReference(), comp1.ToMetadataReference()];
var src2 = @"
using System.Diagnostics.CodeAnalysis;
class C : I
{
[UnscopedRef]
public ref int M()
{
throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp2.VerifyDiagnostics(
// (6,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(6, 6)
);
Assert.False(comp2.GetMember<MethodSymbol>("C.M").HasUnscopedRefAttribute);
}
var src3 = @"
using System.Diagnostics.CodeAnalysis;
class C : I
{
[UnscopedRef]
ref int I.M()
{
throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp3 = CreateCompilation(src3, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp3.VerifyDiagnostics(
// (6,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(6, 6)
);
Assert.False(comp3.GetMember<MethodSymbol>("C.I.M").HasUnscopedRefAttribute);
}
var src4 = @"
class C1 : I
{
int f = 0;
public ref int M()
{
return ref f;
}
}
class C2 : I
{
int f = 0;
ref int I.M()
{
return ref f;
}
}
class C3
{
int f = 0;
public ref int M()
{
return ref f;
}
}
class C4 : C3, I {}
";
foreach (var comp1Ref in comp1Refs)
{
var comp4 = CreateCompilation(src4, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp4, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
Assert.False(m.GlobalNamespace.GetMember<MethodSymbol>("C1.M").HasUnscopedRefAttribute);
Assert.False(m.GlobalNamespace.GetMember<MethodSymbol>("C2.I.M").HasUnscopedRefAttribute);
Assert.False(m.GlobalNamespace.GetMember<MethodSymbol>("C3.M").HasUnscopedRefAttribute);
}
}
var src5 = @"
using System.Diagnostics.CodeAnalysis;
interface C : I
{
[UnscopedRef]
ref int I.M()
{
throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp5 = CreateCompilation(src5, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp5.VerifyDiagnostics(
// (6,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(6, 6)
);
Assert.False(comp5.GetMember<MethodSymbol>("C.I.M").HasUnscopedRefAttribute);
}
var src6 = @"
interface C : I
{
ref int I.M()
{
throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp6 = CreateCompilation(src6, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp6, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
Assert.False(m.GlobalNamespace.GetMember<MethodSymbol>("C.I.M").HasUnscopedRefAttribute);
}
}
var src7 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
[UnscopedRef]
public ref int M()
{
return ref f;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp7 = CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp7, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
Assert.True(m.GlobalNamespace.GetMember<MethodSymbol>("C.M").HasUnscopedRefAttribute);
}
CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (8,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(8, 6)
);
}
var src8 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
[UnscopedRef]
ref int I.M()
{
return ref f;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp8 = CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp8, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
Assert.True(m.GlobalNamespace.GetMember<MethodSymbol>("C.I.M").HasUnscopedRefAttribute);
}
CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (8,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(8, 6)
);
}
var src9 = @"
public struct C : I
{
public ref int M()
{
throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp9 = CreateCompilation(src9, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp9, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
Assert.False(m.GlobalNamespace.GetMember<MethodSymbol>("C.M").HasUnscopedRefAttribute);
}
}
var src10 = @"
public struct C : I
{
ref int I.M()
{
throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp10 = CreateCompilation(src10, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp10, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
Assert.False(m.GlobalNamespace.GetMember<MethodSymbol>("C.I.M").HasUnscopedRefAttribute);
}
}
var src11 = @"
public struct C : I
{
public int f;
public ref int M()
{
return ref f;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp11 = CreateCompilation(src11, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp11.VerifyDiagnostics(
// (8,20): error CS8170: Struct members cannot return 'this' or other instance members by reference
// return ref f;
Diagnostic(ErrorCode.ERR_RefReturnStructThis, "f").WithLocation(8, 20)
);
}
var src12 = @"
public struct C : I
{
public int f;
ref int I.M()
{
return ref f;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp12 = CreateCompilation(src12, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp12.VerifyDiagnostics(
// (8,20): error CS8170: Struct members cannot return 'this' or other instance members by reference
// return ref f;
Diagnostic(ErrorCode.ERR_RefReturnStructThis, "f").WithLocation(8, 20)
);
}
}
[Fact]
public void UnscopedRefInImplementation_Method_02()
{
var src1 = @"
public interface I
{
ref int M();
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80);
MetadataReference[] comp1Refs = [comp1.EmitToImageReference(), comp1.ToMetadataReference()];
var src7 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
[UnscopedRef]
public ref int M()
{
return ref f;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp7 = CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp7.VerifyDiagnostics(
// (9,20): error CS9102: UnscopedRefAttribute cannot be applied to an interface implementation because implemented member 'I.M()' doesn't have this attribute.
// public ref int M()
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeInterfaceImplementation, "M").WithArguments("I.M()").WithLocation(9, 20)
);
Assert.True(comp7.GetMember<MethodSymbol>("C.M").HasUnscopedRefAttribute);
}
var src8 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
[UnscopedRef]
ref int I.M()
{
return ref f;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp8 = CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp8.VerifyDiagnostics(
// (9,15): error CS9102: UnscopedRefAttribute cannot be applied to an interface implementation because implemented member 'I.M()' doesn't have this attribute.
// ref int I.M()
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeInterfaceImplementation, "M").WithArguments("I.M()").WithLocation(9, 15)
);
Assert.True(comp8.GetMember<MethodSymbol>("C.I.M").HasUnscopedRefAttribute);
}
}
[Fact]
public void UnscopedRefInImplementation_Method_03()
{
var src = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
#line 100
[UnscopedRef]
ref int M();
}
public struct C : I
{
public int f;
#line 200
[UnscopedRef]
public ref int M()
{
return ref f;
}
}
";
CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (100,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(100, 6)
);
}
[Theory]
[CombinatorialData]
public void UnscopedRefInImplementation_Property_01(bool onInterfaceProperty, bool onInterfaceGet, bool onImplementationProperty, bool onImplementationGet)
{
if (!onInterfaceProperty && !onInterfaceGet)
{
return;
}
var src1 = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
" + (onInterfaceProperty ? "[UnscopedRef]" : "") + @"
ref int P { " + (onInterfaceGet ? "[UnscopedRef] " : "") + @"get; }
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80);
var p = comp1.GetMember<PropertySymbol>("I.P");
Assert.Equal(onInterfaceProperty, p.HasUnscopedRefAttribute);
Assert.Equal(onInterfaceGet, p.GetMethod.HasUnscopedRefAttribute);
MetadataReference[] comp1Refs = [comp1.EmitToImageReference(), comp1.ToMetadataReference()];
if (onImplementationProperty || onImplementationGet)
{
var src2 = @"
using System.Diagnostics.CodeAnalysis;
class C : I
{
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
public ref int P
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
=> throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80);
if (onImplementationProperty)
{
if (onImplementationGet)
{
comp2.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6),
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
else
{
comp2.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6)
);
}
}
else
{
comp2.VerifyDiagnostics(
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
PropertySymbol propertySymbol = comp2.GetMember<PropertySymbol>("C.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
var src3 = @"
using System.Diagnostics.CodeAnalysis;
class C : I
{
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
ref int I. P
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
=> throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp3 = CreateCompilation(src3, references: [comp1Ref], targetFramework: TargetFramework.Net80);
if (onImplementationProperty)
{
if (onImplementationGet)
{
comp3.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6),
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
else
{
comp3.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6)
);
}
}
else
{
comp3.VerifyDiagnostics(
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
PropertySymbol propertySymbol = comp3.GetMember<PropertySymbol>("C.I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
if (!onImplementationProperty && !onImplementationGet)
{
var src4 = @"
class C1 : I
{
int f = 0;
public ref int P
{ get{
return ref f;
}}
}
class C2 : I
{
int f = 0;
ref int I.P
{ get{
return ref f;
}}
}
class C3
{
int f = 0;
public ref int P
{ get{
return ref f;
}}
}
class C4 : C3, I {}
";
foreach (var comp1Ref in comp1Refs)
{
var comp4 = CreateCompilation(src4, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp4, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol c1P = m.GlobalNamespace.GetMember<PropertySymbol>("C1.P");
Assert.False(c1P.HasUnscopedRefAttribute);
Assert.False(c1P.GetMethod.HasUnscopedRefAttribute);
PropertySymbol c2P = m.GlobalNamespace.GetMember<PropertySymbol>("C2.I.P");
Assert.False(c2P.HasUnscopedRefAttribute);
Assert.False(c2P.GetMethod.HasUnscopedRefAttribute);
PropertySymbol c3P = m.GlobalNamespace.GetMember<PropertySymbol>("C3.P");
Assert.False(c3P.HasUnscopedRefAttribute);
Assert.False(c3P.GetMethod.HasUnscopedRefAttribute);
}
}
}
if (onImplementationProperty || onImplementationGet)
{
var src5 = @"
using System.Diagnostics.CodeAnalysis;
interface C : I
{
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
ref int I.P
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
=> throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp5 = CreateCompilation(src5, references: [comp1Ref], targetFramework: TargetFramework.Net80);
if (onImplementationProperty)
{
if (onImplementationGet)
{
comp5.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6),
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
else
{
comp5.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6)
);
}
}
else
{
comp5.VerifyDiagnostics(
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
PropertySymbol propertySymbol = comp5.GetMember<PropertySymbol>("C.I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
if (!onImplementationProperty && !onImplementationGet)
{
var src6 = @"
interface C : I
{
ref int I.P => throw null;
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp6 = CreateCompilation(src6, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp6, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C.I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
}
if (onImplementationProperty || onImplementationGet)
{
var src7 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
public ref int P
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
{
return ref f;
}
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp7 = CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp7, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C.P");
Assert.Equal(onImplementationProperty, propertySymbol.HasUnscopedRefAttribute);
Assert.Equal(onImplementationGet, propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
comp7 = CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12);
if (onImplementationGet)
{
comp7.VerifyDiagnostics(
// (200,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(200, 10)
);
}
else
{
comp7.VerifyDiagnostics(
// (100,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(100, 6)
);
}
}
var src8 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
ref int I.P
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
{
return ref f;
}
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp8 = CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp8, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C.I.P");
Assert.Equal(onImplementationProperty, propertySymbol.HasUnscopedRefAttribute);
Assert.Equal(onImplementationGet, propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
comp8 = CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12);
if (onImplementationProperty)
{
if (onImplementationGet)
{
comp8.VerifyDiagnostics(
// (100,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(100, 6),
// (200,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(200, 10)
);
}
else
{
comp8.VerifyDiagnostics(
// (100,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(100, 6)
);
}
}
else
{
comp8.VerifyDiagnostics(
// (200,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(200, 10)
);
}
}
}
if (!onImplementationProperty && !onImplementationGet)
{
var src9 = @"
public struct C : I
{
public ref int P => throw null;
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp9 = CreateCompilation(src9, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp9, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
var src10 = @"
public struct C : I
{
ref int I.P => throw null;
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp10 = CreateCompilation(src10, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp10, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C.I.P");
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
var src11 = @"
public struct C : I
{
public int f;
public ref int P
{ get{
return ref f;
}}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp11 = CreateCompilation(src11, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp11.VerifyDiagnostics(
// (8,20): error CS8170: Struct members cannot return 'this' or other instance members by reference
// return ref f;
Diagnostic(ErrorCode.ERR_RefReturnStructThis, "f").WithLocation(8, 20)
);
}
var src12 = @"
public struct C : I
{
public int f;
ref int I.P
{ get{
return ref f;
}}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp12 = CreateCompilation(src12, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp12.VerifyDiagnostics(
// (8,20): error CS8170: Struct members cannot return 'this' or other instance members by reference
// return ref f;
Diagnostic(ErrorCode.ERR_RefReturnStructThis, "f").WithLocation(8, 20)
);
}
}
}
[Theory]
[CombinatorialData]
public void UnscopedRefInImplementation_Property_02(bool onProperty, bool onGet)
{
if (!onProperty && !onGet)
{
return;
}
var src1 = @"
public interface I
{
ref int P { get; }
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80);
MetadataReference[] comp1Refs = [comp1.EmitToImageReference(), comp1.ToMetadataReference()];
var src7 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
" + (onProperty ? "[UnscopedRef]" : "") + @"
public ref int P
{
#line 200
" + (onGet ? "[UnscopedRef] " : "") + @"
get
{
return ref f;
}
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp7 = CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp7.VerifyDiagnostics(
// (201,9): error CS9102: UnscopedRefAttribute cannot be applied to an interface implementation because implemented member 'I.P.get' doesn't have this attribute.
// get
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeInterfaceImplementation, "get").WithArguments("I.P.get").WithLocation(201, 9)
);
PropertySymbol propertySymbol = comp7.GetMember<PropertySymbol>("C.P");
Assert.Equal(onProperty, propertySymbol.HasUnscopedRefAttribute);
Assert.Equal(onGet, propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
var src8 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
" + (onProperty ? "[UnscopedRef]" : "") + @"
ref int I.P
{
#line 200
" + (onGet ? "[UnscopedRef] " : "") + @"
get
{
return ref f;
}
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp8 = CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp8.VerifyDiagnostics(
// (201,9): error CS9102: UnscopedRefAttribute cannot be applied to an interface implementation because implemented member 'I.P.get' doesn't have this attribute.
// get
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeInterfaceImplementation, "get").WithArguments("I.P.get").WithLocation(201, 9)
);
PropertySymbol propertySymbol = comp8.GetMember<PropertySymbol>("C.I.P");
Assert.Equal(onProperty, propertySymbol.HasUnscopedRefAttribute);
Assert.Equal(onGet, propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
[Theory]
[CombinatorialData]
public void UnscopedRefInImplementation_Indexer_01(bool onInterfaceProperty, bool onInterfaceGet, bool onImplementationProperty, bool onImplementationGet)
{
if (!onInterfaceProperty && !onInterfaceGet)
{
return;
}
var src1 = @"
using System.Diagnostics.CodeAnalysis;
public interface I
{
" + (onInterfaceProperty ? "[UnscopedRef]" : "") + @"
ref int this[int i] { " + (onInterfaceGet ? "[UnscopedRef] " : "") + @"get; }
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80);
var p = comp1.GetMember<PropertySymbol>("I." + WellKnownMemberNames.Indexer);
Assert.Equal(onInterfaceProperty, p.HasUnscopedRefAttribute);
Assert.Equal(onInterfaceGet, p.GetMethod.HasUnscopedRefAttribute);
MetadataReference[] comp1Refs = [comp1.EmitToImageReference(), comp1.ToMetadataReference()];
if (onImplementationProperty || onImplementationGet)
{
var src2 = @"
using System.Diagnostics.CodeAnalysis;
class C : I
{
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
public ref int this[int i]
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
=> throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80);
if (onImplementationProperty)
{
if (onImplementationGet)
{
comp2.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6),
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
else
{
comp2.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6)
);
}
}
else
{
comp2.VerifyDiagnostics(
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
PropertySymbol propertySymbol = comp2.GetMember<PropertySymbol>("C." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
var src3 = @"
using System.Diagnostics.CodeAnalysis;
class C : I
{
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
ref int I. this[int i]
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
=> throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp3 = CreateCompilation(src3, references: [comp1Ref], targetFramework: TargetFramework.Net80);
if (onImplementationProperty)
{
if (onImplementationGet)
{
comp3.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6),
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
else
{
comp3.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6)
);
}
}
else
{
comp3.VerifyDiagnostics(
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
PropertySymbol propertySymbol = comp3.GetMember<PropertySymbol>("C.I." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
if (!onImplementationProperty && !onImplementationGet)
{
var src4 = @"
class C1 : I
{
int f = 0;
public ref int this[int i]
{ get{
return ref f;
}}
}
class C2 : I
{
int f = 0;
ref int I.this[int i]
{ get{
return ref f;
}}
}
class C3
{
int f = 0;
public ref int this[int i]
{ get{
return ref f;
}}
}
class C4 : C3, I {}
";
foreach (var comp1Ref in comp1Refs)
{
var comp4 = CreateCompilation(src4, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp4, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol c1P = m.GlobalNamespace.GetMember<PropertySymbol>("C1." + WellKnownMemberNames.Indexer);
Assert.False(c1P.HasUnscopedRefAttribute);
Assert.False(c1P.GetMethod.HasUnscopedRefAttribute);
PropertySymbol c2P = m.GlobalNamespace.GetMember<PropertySymbol>("C2.I." + (m is PEModuleSymbol ? "Item" : WellKnownMemberNames.Indexer));
Assert.False(c2P.HasUnscopedRefAttribute);
Assert.False(c2P.GetMethod.HasUnscopedRefAttribute);
PropertySymbol c3P = m.GlobalNamespace.GetMember<PropertySymbol>("C3." + WellKnownMemberNames.Indexer);
Assert.False(c3P.HasUnscopedRefAttribute);
Assert.False(c3P.GetMethod.HasUnscopedRefAttribute);
}
}
}
if (onImplementationProperty || onImplementationGet)
{
var src5 = @"
using System.Diagnostics.CodeAnalysis;
interface C : I
{
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
ref int I.this[int i]
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
=> throw null;
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp5 = CreateCompilation(src5, references: [comp1Ref], targetFramework: TargetFramework.Net80);
if (onImplementationProperty)
{
if (onImplementationGet)
{
comp5.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6),
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
else
{
comp5.VerifyDiagnostics(
// (100,6): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(100, 6)
);
}
}
else
{
comp5.VerifyDiagnostics(
// (200,10): error CS9101: UnscopedRefAttribute can only be applied to struct or virtual interface instance methods and properties, and cannot be applied to constructors or init-only members.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(200, 10)
);
}
PropertySymbol propertySymbol = comp5.GetMember<PropertySymbol>("C.I." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
if (!onImplementationProperty && !onImplementationGet)
{
var src6 = @"
interface C : I
{
ref int I.this[int i] => throw null;
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp6 = CreateCompilation(src6, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp6, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C.I." + (m is PEModuleSymbol ? "Item" : WellKnownMemberNames.Indexer));
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
}
if (onImplementationProperty || onImplementationGet)
{
var src7 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
public ref int this[int i]
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
{
return ref f;
}
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp7 = CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp7, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C." + WellKnownMemberNames.Indexer);
Assert.Equal(onImplementationProperty, propertySymbol.HasUnscopedRefAttribute);
Assert.Equal(onImplementationGet, propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
comp7 = CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12);
if (onImplementationGet)
{
comp7.VerifyDiagnostics(
// (200,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(200, 10)
);
}
else
{
comp7.VerifyDiagnostics(
// (100,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(100, 6)
);
}
}
var src8 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
#line 100
" + (onImplementationProperty ? "[UnscopedRef]" : "") + @"
ref int I.this[int i]
{
#line 200
" + (onImplementationGet ? "[UnscopedRef] " : "") + @"
get
{
return ref f;
}
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp8 = CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp8, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C.I." + (m is PEModuleSymbol ? "Item" : WellKnownMemberNames.Indexer));
Assert.Equal(onImplementationProperty, propertySymbol.HasUnscopedRefAttribute);
Assert.Equal(onImplementationGet, propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
comp8 = CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12);
if (onImplementationProperty)
{
if (onImplementationGet)
{
comp8.VerifyDiagnostics(
// (100,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(100, 6),
// (200,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(200, 10)
);
}
else
{
comp8.VerifyDiagnostics(
// (100,6): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(100, 6)
);
}
}
else
{
comp8.VerifyDiagnostics(
// (200,10): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// [UnscopedRef]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "UnscopedRef").WithArguments("ref struct interfaces", "13.0").WithLocation(200, 10)
);
}
}
}
if (!onImplementationProperty && !onImplementationGet)
{
var src9 = @"
public struct C : I
{
public ref int this[int i] => throw null;
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp9 = CreateCompilation(src9, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp9, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C." + WellKnownMemberNames.Indexer);
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
var src10 = @"
public struct C : I
{
ref int I.this[int i] => throw null;
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp10 = CreateCompilation(src10, references: [comp1Ref], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp10, sourceSymbolValidator: verify, symbolValidator: verify, verify: Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
PropertySymbol propertySymbol = m.GlobalNamespace.GetMember<PropertySymbol>("C.I." + (m is PEModuleSymbol ? "Item" : WellKnownMemberNames.Indexer));
Assert.False(propertySymbol.HasUnscopedRefAttribute);
Assert.False(propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
var src11 = @"
public struct C : I
{
public int f;
public ref int this[int i]
{ get{
return ref f;
}}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp11 = CreateCompilation(src11, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp11.VerifyDiagnostics(
// (8,20): error CS8170: Struct members cannot return 'this' or other instance members by reference
// return ref f;
Diagnostic(ErrorCode.ERR_RefReturnStructThis, "f").WithLocation(8, 20)
);
}
var src12 = @"
public struct C : I
{
public int f;
ref int I.this[int i]
{ get{
return ref f;
}}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp12 = CreateCompilation(src12, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp12.VerifyDiagnostics(
// (8,20): error CS8170: Struct members cannot return 'this' or other instance members by reference
// return ref f;
Diagnostic(ErrorCode.ERR_RefReturnStructThis, "f").WithLocation(8, 20)
);
}
}
}
[Theory]
[CombinatorialData]
public void UnscopedRefInImplementation_Indexer_02(bool onProperty, bool onGet)
{
if (!onProperty && !onGet)
{
return;
}
var src1 = @"
public interface I
{
ref int this[int i] { get; }
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80);
MetadataReference[] comp1Refs = [comp1.EmitToImageReference(), comp1.ToMetadataReference()];
var src7 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
" + (onProperty ? "[UnscopedRef]" : "") + @"
public ref int this[int i]
{
#line 200
" + (onGet ? "[UnscopedRef] " : "") + @"
get
{
return ref f;
}
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp7 = CreateCompilation(src7, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp7.VerifyDiagnostics(
// (201,9): error CS9102: UnscopedRefAttribute cannot be applied to an interface implementation because implemented member 'I.P.get' doesn't have this attribute.
// get
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeInterfaceImplementation, "get").WithArguments("I.this[int].get").WithLocation(201, 9)
);
PropertySymbol propertySymbol = comp7.GetMember<PropertySymbol>("C." + WellKnownMemberNames.Indexer);
Assert.Equal(onProperty, propertySymbol.HasUnscopedRefAttribute);
Assert.Equal(onGet, propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
var src8 = @"
using System.Diagnostics.CodeAnalysis;
public struct C : I
{
public int f;
" + (onProperty ? "[UnscopedRef]" : "") + @"
ref int I.this[int i]
{
#line 200
" + (onGet ? "[UnscopedRef] " : "") + @"
get
{
return ref f;
}
}
}
";
foreach (var comp1Ref in comp1Refs)
{
var comp8 = CreateCompilation(src8, references: [comp1Ref], targetFramework: TargetFramework.Net80);
comp8.VerifyDiagnostics(
// (201,9): error CS9102: UnscopedRefAttribute cannot be applied to an interface implementation because implemented member 'I.P.get' doesn't have this attribute.
// get
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeInterfaceImplementation, "get").WithArguments("I.this[int].get").WithLocation(201, 9)
);
PropertySymbol propertySymbol = comp8.GetMember<PropertySymbol>("C.I." + WellKnownMemberNames.Indexer);
Assert.Equal(onProperty, propertySymbol.HasUnscopedRefAttribute);
Assert.Equal(onGet, propertySymbol.GetMethod.HasUnscopedRefAttribute);
}
}
// This is a clone of MethodArgumentsMustMatch_16 from RefFieldTests.cs
[Fact]
public void MethodArgumentsMustMatch_16_DirectInterface()
{
var source = """
using System.Diagnostics.CodeAnalysis;
interface R
{
public ref int FA();
[UnscopedRef] public ref int FB();
}
class Program
{
static void F1(ref R r1, ref int i1) { }
static void F2(ref R r2, [UnscopedRef] ref int i2) { }
static void F(ref R x)
{
R y = default;
F1(ref x, ref y.FA());
F1(ref x, ref y.FB());
F2(ref x, ref y.FA());
F2(ref x, ref y.FB()); // 1
}
}
""";
var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition });
comp.VerifyDiagnostics();
}
// This is a clone of MethodArgumentsMustMatch_16 from RefFieldTests.cs
[Fact]
public void MethodArgumentsMustMatch_16_ConstrainedTypeParameter()
{
var source = """
using System.Diagnostics.CodeAnalysis;
interface R
{
public ref int FA();
[UnscopedRef] public ref int FB();
}
class Program<T> where T : R, allows ref struct
{
static void F1(ref T r1, ref int i1) { }
static void F2(ref T r2, [UnscopedRef] ref int i2) { }
static void F(ref T x)
{
T y = default;
F1(ref x, ref y.FA());
F1(ref x, ref y.FB());
F2(ref x, ref y.FA());
F2(ref x, ref y.FB()); // 1
}
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (17,9): error CS8350: This combination of arguments to 'Program<T>.F2(ref T, ref int)' is disallowed because it may expose variables referenced by parameter 'i2' outside of their declaration scope
// F2(ref x, ref y.FB()); // 1
Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref x, ref y.FB())").WithArguments("Program<T>.F2(ref T, ref int)", "i2").WithLocation(17, 9),
// (17,23): error CS8168: Cannot return local 'y' by reference because it is not a ref local
// F2(ref x, ref y.FB()); // 1
Diagnostic(ErrorCode.ERR_RefReturnLocal, "y").WithArguments("y").WithLocation(17, 23));
}
// This is a clone of MethodArgumentsMustMatch_16 from RefFieldTests.cs
[Fact]
public void MethodArgumentsMustMatch_16_ClassConstrainedTypeParameter()
{
var source = """
using System.Diagnostics.CodeAnalysis;
interface R
{
public ref int FA();
[UnscopedRef] public ref int FB();
}
class Program<T> where T : class, R
{
static void F1(ref T r1, ref int i1) { }
static void F2(ref T r2, [UnscopedRef] ref int i2) { }
static void F(ref T x)
{
T y = default;
F1(ref x, ref y.FA());
F1(ref x, ref y.FB());
F2(ref x, ref y.FA());
F2(ref x, ref y.FB()); // 1
}
}
""";
var comp = CreateCompilation(new[] { source, UnscopedRefAttributeDefinition });
comp.VerifyDiagnostics();
}
// This is a clone of MethodArgumentsMustMatch_17 from RefFieldTests.cs
[Fact]
public void MethodArgumentsMustMatch_17()
{
var source = """
using System.Diagnostics.CodeAnalysis;
interface IR
{
public ref int FA();
[UnscopedRef] public ref int FB();
}
class Program<R> where R : IR, allows ref struct
{
static void F1(ref R r1, in int i1) { }
static void F2(ref R r2, [UnscopedRef] in int i2) { }
static void F(ref R x)
{
R y = default;
F1(ref x, y.FA());
F1(ref x, y.FB());
F2(ref x, y.FA());
F2(ref x, y.FB()); // 1
F1(ref x, in y.FA());
F1(ref x, in y.FB());
F2(ref x, in y.FA());
F2(ref x, in y.FB()); // 2
}
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (17,9): error CS8350: This combination of arguments to 'Program<R>.F2(ref R, in int)' is disallowed because it may expose variables referenced by parameter 'i2' outside of their declaration scope
// F2(ref x, y.FB()); // 1
Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref x, y.FB())").WithArguments("Program<R>.F2(ref R, in int)", "i2").WithLocation(17, 9),
// (17,19): error CS8168: Cannot return local 'y' by reference because it is not a ref local
// F2(ref x, y.FB()); // 1
Diagnostic(ErrorCode.ERR_RefReturnLocal, "y").WithArguments("y").WithLocation(17, 19),
// (21,9): error CS8350: This combination of arguments to 'Program<R>.F2(ref R, in int)' is disallowed because it may expose variables referenced by parameter 'i2' outside of their declaration scope
// F2(ref x, in y.FB()); // 2
Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref x, in y.FB())").WithArguments("Program<R>.F2(ref R, in int)", "i2").WithLocation(21, 9),
// (21,22): error CS8168: Cannot return local 'y' by reference because it is not a ref local
// F2(ref x, in y.FB()); // 2
Diagnostic(ErrorCode.ERR_RefReturnLocal, "y").WithArguments("y").WithLocation(21, 22));
}
// This is a clone of MethodArgumentsMustMatch_18 from RefFieldTests.cs
[Fact]
public void MethodArgumentsMustMatch_18()
{
var source = """
using System.Diagnostics.CodeAnalysis;
interface IR
{
public ref readonly int FA();
[UnscopedRef] public ref readonly int FB();
}
class Program<R> where R : IR, allows ref struct
{
static void F1(ref R r1, in int i1) { }
static void F2(ref R r2, [UnscopedRef] in int i2) { }
static void F(ref R x)
{
R y = default;
F1(ref x, y.FA());
F1(ref x, y.FB());
F2(ref x, y.FA());
F2(ref x, y.FB()); // 1
F1(ref x, in y.FA());
F1(ref x, in y.FB());
F2(ref x, in y.FA());
F2(ref x, in y.FB()); // 2
}
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (17,9): error CS8350: This combination of arguments to 'Program<R>.F2(ref R, in int)' is disallowed because it may expose variables referenced by parameter 'i2' outside of their declaration scope
// F2(ref x, y.FB()); // 1
Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref x, y.FB())").WithArguments("Program<R>.F2(ref R, in int)", "i2").WithLocation(17, 9),
// (17,19): error CS8168: Cannot return local 'y' by reference because it is not a ref local
// F2(ref x, y.FB()); // 1
Diagnostic(ErrorCode.ERR_RefReturnLocal, "y").WithArguments("y").WithLocation(17, 19),
// (21,9): error CS8350: This combination of arguments to 'Program<R>.F2(ref R, in int)' is disallowed because it may expose variables referenced by parameter 'i2' outside of their declaration scope
// F2(ref x, in y.FB()); // 2
Diagnostic(ErrorCode.ERR_CallArgMixing, "F2(ref x, in y.FB())").WithArguments("Program<R>.F2(ref R, in int)", "i2").WithLocation(21, 9),
// (21,22): error CS8168: Cannot return local 'y' by reference because it is not a ref local
// F2(ref x, in y.FB()); // 2
Diagnostic(ErrorCode.ERR_RefReturnLocal, "y").WithArguments("y").WithLocation(21, 22));
}
// This is a clone of ReturnOnlyScope_01 from RefFieldTests.cs
[Fact]
public void ReturnOnlyScope_01()
{
// test that return scope is used in all return-ey locations.
var source = """
using System.Diagnostics.CodeAnalysis;
interface IRS<RSOut> where RSOut : IRSOut, allows ref struct
{
[UnscopedRef]
public RSOut ToRSOut();
}
interface IRSOut
{
}
class Program<RS, RSOut> where RS : IRS<RSOut>, allows ref struct where RSOut : IRSOut, allows ref struct
{
RS M1(ref RS rs) => rs;
void M2(ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut();
RS M3(ref RS rs)
{
return rs;
}
void M4(ref RS rs, out RSOut rs1)
{
rs1 = rs.ToRSOut();
}
void localContainer()
{
#pragma warning disable 8321
RS M1(ref RS rs) => rs;
void M2(ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut();
RS M3(ref RS rs)
{
return rs;
}
void M4(ref RS rs, out RSOut rs1)
{
rs1 = rs.ToRSOut(); // 4
}
}
delegate RS ReturnsRefStruct(ref RS rs);
delegate void RefStructOut(ref RS rs, out RSOut rs1);
void lambdaContainer()
{
ReturnsRefStruct d1 = (ref RS rs) => rs;
RefStructOut d2 = (ref RS rs, out RSOut rs1) => rs1 = rs.ToRSOut();
ReturnsRefStruct d3 = (ref RS rs) =>
{
return rs;
};
RefStructOut d4 = (ref RS rs, out RSOut rs1) =>
{
rs1 = rs.ToRSOut();
};
}
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
Assert.True(comp.SupportsRuntimeCapability(RuntimeCapability.ByRefLikeGenerics));
}
// This is a clone of ReturnRefToRefStruct_ValEscape_01 from RefFieldTests.cs
[Fact]
public void ReturnRefToRefStruct_ValEscape_01()
{
var source = """
using System.Diagnostics.CodeAnalysis;
class Repro<TRefStruct> where TRefStruct : IRefStruct, new() , allows ref struct
{
private static void Bad2(int value)
{
TRefStruct s1 = new TRefStruct();
s1.RefProperty.RefField = ref value; // 2
}
private static void Bad3(int value)
{
TRefStruct s1 = new TRefStruct();
s1.RefMethod().RefField = ref value; // 3
}
}
ref struct RefStruct
{
public ref int RefField;
}
interface IRefStruct
{
[UnscopedRef] public ref RefStruct RefProperty {get;}
[UnscopedRef] public ref RefStruct RefMethod();
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (8,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'.
// s1.RefProperty.RefField = ref value; // 2
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s1.RefProperty.RefField = ref value").WithArguments("RefField", "value").WithLocation(8, 9),
// (14,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'.
// s1.RefMethod().RefField = ref value; // 3
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s1.RefMethod().RefField = ref value").WithArguments("RefField", "value").WithLocation(14, 9));
}
// This is a clone of ReturnRefToRefStruct_ValEscape_02 from RefFieldTests.cs
[Fact]
public void ReturnRefToRefStruct_ValEscape_02()
{
var source = """
using System.Diagnostics.CodeAnalysis;
class Repro<TRefStruct> where TRefStruct : IRefStruct, allows ref struct
{
private static void Bad2(scoped ref TRefStruct s1, int value)
{
s1.RefProperty.RefField = ref value; // 2
}
private static void Bad3(scoped ref TRefStruct s1, int value)
{
s1.RefMethod().RefField = ref value; // 3
}
private static void Bad5(scoped in TRefStruct s1, int value)
{
s1.RefProperty.RefField = ref value; // 5
}
private static void Bad6(scoped in TRefStruct s1, int value)
{
s1.RefMethod().RefField = ref value; // 6
}
private static void Bad8(in TRefStruct s1, int value)
{
s1.RefProperty.RefField = ref value; // 8
}
private static void Bad9(in TRefStruct s1, int value)
{
s1.RefMethod().RefField = ref value; // 9
}
}
ref struct RefStruct
{
public ref int RefField;
}
interface IRefStruct
{
[UnscopedRef] public ref RefStruct RefProperty {get;}
[UnscopedRef] public ref RefStruct RefMethod();
}
""";
// NB: 8 and 9 are not strictly necessary here because they are assigning to an implicit copy of a readonly variable, not to the original variable.
// However, it is not deeply problematic that an error is given here.
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'.
// s1.RefProperty.RefField = ref value; // 2
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s1.RefProperty.RefField = ref value").WithArguments("RefField", "value").WithLocation(7, 9),
// (12,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'.
// s1.RefMethod().RefField = ref value; // 3
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s1.RefMethod().RefField = ref value").WithArguments("RefField", "value").WithLocation(12, 9),
// (17,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'.
// s1.RefProperty.RefField = ref value; // 5
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s1.RefProperty.RefField = ref value").WithArguments("RefField", "value").WithLocation(17, 9),
// (22,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'.
// s1.RefMethod().RefField = ref value; // 6
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s1.RefMethod().RefField = ref value").WithArguments("RefField", "value").WithLocation(22, 9),
// (27,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'.
// s1.RefProperty.RefField = ref value; // 8
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s1.RefProperty.RefField = ref value").WithArguments("RefField", "value").WithLocation(27, 9),
// (32,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'.
// s1.RefMethod().RefField = ref value; // 9
Diagnostic(ErrorCode.ERR_RefAssignNarrower, "s1.RefMethod().RefField = ref value").WithArguments("RefField", "value").WithLocation(32, 9)
);
}
// This is a clone of ReturnRefToRefStruct_ValEscape_03 from RefFieldTests.cs
[Fact]
public void ReturnRefToRefStruct_ValEscape_03()
{
var source = """
using System.Diagnostics.CodeAnalysis;
class Repro<TRefStruct> where TRefStruct : IRefStruct<TRefStruct>, allows ref struct
{
private static void Bad1(ref TRefStruct s1, int value)
{
s1 = TRefStruct.New(ref value); // 1
}
private static void Bad2(scoped ref TRefStruct s1, int value)
{
s1.RefProperty = TRefStruct.New(ref value); // 2
}
private static void Bad3(scoped ref TRefStruct s1, int value)
{
s1.RefMethod() = TRefStruct.New(ref value); // 3
}
private static void Bad4(scoped ref TRefStruct s1, int value)
{
GetRef(ref s1) = TRefStruct.New(ref value); // 4
}
private static ref TRefStruct GetRef(ref TRefStruct s) => ref s;
}
interface IRefStruct<TRefStruct> where TRefStruct : IRefStruct<TRefStruct>, allows ref struct
{
abstract static TRefStruct New(ref int i);
[UnscopedRef] public ref TRefStruct RefProperty {get;}
[UnscopedRef] public ref TRefStruct RefMethod();
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,14): error CS8347: Cannot use a result of 'IRefStruct<TRefStruct>.New(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope
// s1 = TRefStruct.New(ref value); // 1
Diagnostic(ErrorCode.ERR_EscapeCall, "TRefStruct.New(ref value)").WithArguments("IRefStruct<TRefStruct>.New(ref int)", "i").WithLocation(7, 14),
// (7,33): error CS8166: Cannot return a parameter by reference 'value' because it is not a ref parameter
// s1 = TRefStruct.New(ref value); // 1
Diagnostic(ErrorCode.ERR_RefReturnParameter, "value").WithArguments("value").WithLocation(7, 33),
// (12,26): error CS8347: Cannot use a result of 'IRefStruct<TRefStruct>.New(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope
// s1.RefProperty = TRefStruct.New(ref value); // 2
Diagnostic(ErrorCode.ERR_EscapeCall, "TRefStruct.New(ref value)").WithArguments("IRefStruct<TRefStruct>.New(ref int)", "i").WithLocation(12, 26),
// (12,45): error CS8166: Cannot return a parameter by reference 'value' because it is not a ref parameter
// s1.RefProperty = TRefStruct.New(ref value); // 2
Diagnostic(ErrorCode.ERR_RefReturnParameter, "value").WithArguments("value").WithLocation(12, 45),
// (17,26): error CS8347: Cannot use a result of 'IRefStruct<TRefStruct>.New(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope
// s1.RefMethod() = TRefStruct.New(ref value); // 3
Diagnostic(ErrorCode.ERR_EscapeCall, "TRefStruct.New(ref value)").WithArguments("IRefStruct<TRefStruct>.New(ref int)", "i").WithLocation(17, 26),
// (17,45): error CS8166: Cannot return a parameter by reference 'value' because it is not a ref parameter
// s1.RefMethod() = TRefStruct.New(ref value); // 3
Diagnostic(ErrorCode.ERR_RefReturnParameter, "value").WithArguments("value").WithLocation(17, 45),
// (22,26): error CS8347: Cannot use a result of 'IRefStruct<TRefStruct>.New(ref int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope
// GetRef(ref s1) = TRefStruct.New(ref value); // 4
Diagnostic(ErrorCode.ERR_EscapeCall, "TRefStruct.New(ref value)").WithArguments("IRefStruct<TRefStruct>.New(ref int)", "i").WithLocation(22, 26),
// (22,45): error CS8166: Cannot return a parameter by reference 'value' because it is not a ref parameter
// GetRef(ref s1) = TRefStruct.New(ref value); // 4
Diagnostic(ErrorCode.ERR_RefReturnParameter, "value").WithArguments("value").WithLocation(22, 45)
);
}
// This is a clone of ReturnRefToRefStruct_ValEscape_04 from RefFieldTests.cs
[Fact]
public void ReturnRefToRefStruct_ValEscape_04()
{
// test that the appropriate filtering of escape-values is occurring when the RTRS expression is on the RHS of an an assignment.
var source = """
using System.Diagnostics.CodeAnalysis;
class Repro<TRefStruct> where TRefStruct : IRefStruct<TRefStruct>, allows ref struct
{
private static void M1(ref TRefStruct s1, int value)
{
// 's2' only contributes STE, not RSTE, to the STE of 'RefMethod()' invocation.
// STE is equal to RSTE for 's2', so it doesn't matter.
var s2 = TRefStruct.New(ref value);
s1 = s2.RefMethod(); // 1
}
private static void M2(ref TRefStruct s1, ref TRefStruct s2)
{
// 's2' only contributes STE, not RSTE, to the STE of 'RefMethod()' invocation.
// RSTE of `s2` is narrower than STE of 's1', but STE of 's2' equals STE of 's1', so we expect no error here.
s1 = s2.RefMethod();
}
}
interface IRefStruct<TRefStruct> where TRefStruct : IRefStruct<TRefStruct>, allows ref struct
{
abstract static TRefStruct New(ref int i);
[UnscopedRef] public ref TRefStruct RefMethod();
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,14): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope
// s1 = s2.RefMethod(); // 1
Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(10, 14));
}
// This is a clone of LocalScope_DeclarationExpression_06 from RefEscapingTests.cs
[Fact]
public void LocalScope_DeclarationExpression_06()
{
var source = """
using System.Diagnostics.CodeAnalysis;
interface IRS<RS> where RS : IRS<RS>, allows ref struct
{
[UnscopedRef]
void M0(out RS rs2);
RS M1()
{
// RSTE of `this` is CurrentMethod
// STE of rs4 (local variable) is also CurrentMethod
M0(out var rs4);
return rs4; // 1
}
[UnscopedRef]
RS M2()
{
M0(out var rs4);
return rs4;
}
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
}
// This is a clone of UnscopedRefAttribute_Method_03 from RefFieldTests.cs
[CombinatorialData]
[Theory]
public void UnscopedRefAttribute_Method_03_DirectInterface(bool useCompilationReference)
{
var sourceA =
@"using System.Diagnostics.CodeAnalysis;
public interface S<T>
{
public ref T F1();
[UnscopedRef] public ref T F2();
}";
var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition });
comp.VerifyEmitDiagnostics();
var refA = AsReference(comp, useCompilationReference);
var sourceB =
@"class Program
{
static ref int F1()
{
var s = GetS();
return ref s.F1();
}
static ref int F2()
{
var s = GetS();
return ref s.F2(); // 1
}
static S<int> GetS() => throw null;
}";
comp = CreateCompilation(sourceB, references: new[] { refA });
comp.VerifyEmitDiagnostics();
}
// This is a clone of UnscopedRefAttribute_Method_03 from RefFieldTests.cs
[CombinatorialData]
[Theory]
public void UnscopedRefAttribute_Method_03_ConstrainedTypeParameter(bool useCompilationReference, bool addStructConstraint)
{
var sourceA =
@"using System.Diagnostics.CodeAnalysis;
public interface S<T>
{
public ref T F1();
[UnscopedRef] public ref T F2();
}";
var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition });
comp.VerifyEmitDiagnostics();
var refA = AsReference(comp, useCompilationReference);
var sourceB =
@"class Program<T> where T : " + (addStructConstraint ? "struct, " : "") + @"S<int>
{
static ref int F1()
{
var s = GetS();
return ref s.F1();
}
static ref int F2()
{
var s = GetS();
return ref s.F2(); // 1
}
static T GetS() => throw null;
}";
comp = CreateCompilation(sourceB, references: new[] { refA });
comp.VerifyEmitDiagnostics(
// (11,20): error CS8168: Cannot return local 's' by reference because it is not a ref local
// return ref s.F2(); // 1
Diagnostic(ErrorCode.ERR_RefReturnLocal, "s").WithArguments("s").WithLocation(11, 20));
}
// This is a clone of UnscopedRefAttribute_Method_03 from RefFieldTests.cs
[CombinatorialData]
[Theory]
public void UnscopedRefAttribute_Method_03_ClassConstrainedTypeParameter(bool useCompilationReference)
{
var sourceA =
@"using System.Diagnostics.CodeAnalysis;
public interface S<T>
{
public ref T F1();
[UnscopedRef] public ref T F2();
}";
var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition });
comp.VerifyEmitDiagnostics();
var refA = AsReference(comp, useCompilationReference);
var sourceB =
@"class Program<T> where T : class, S<int>
{
static ref int F1()
{
var s = GetS();
return ref s.F1();
}
static ref int F2()
{
var s = GetS();
return ref s.F2(); // 1
}
static T GetS() => throw null;
}";
comp = CreateCompilation(sourceB, references: new[] { refA });
comp.VerifyEmitDiagnostics();
}
// This is a clone of UnscopedRefAttribute_Property_02 from RefFieldTests.cs
[CombinatorialData]
[Theory]
public void UnscopedRefAttribute_Property_02_DirectInterface(bool useCompilationReference)
{
var sourceA =
@"using System.Diagnostics.CodeAnalysis;
public interface S<T>
{
public ref T P1 {get;}
[UnscopedRef] public ref T P2 {get;}
}";
var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition });
comp.VerifyEmitDiagnostics();
var refA = AsReference(comp, useCompilationReference);
var sourceB =
@"class Program
{
static ref int F1()
{
var s = default(S<int>);
return ref s.P1;
}
static ref int F2()
{
var s = default(S<int>);
return ref s.P2; // 1
}
}";
comp = CreateCompilation(sourceB, references: new[] { refA });
comp.VerifyEmitDiagnostics();
}
// This is a clone of UnscopedRefAttribute_Property_02 from RefFieldTests.cs
[CombinatorialData]
[Theory]
public void UnscopedRefAttribute_Property_02_ConstrainedTypeParameter(bool useCompilationReference, bool addStructConstraint)
{
var sourceA =
@"using System.Diagnostics.CodeAnalysis;
public interface S<T>
{
public ref T P1 {get;}
[UnscopedRef] public ref T P2 {get;}
}";
var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition });
comp.VerifyEmitDiagnostics();
var refA = AsReference(comp, useCompilationReference);
var sourceB =
@"class Program<T> where T : " + (addStructConstraint ? "struct, " : "") + @"S<int>" + (!addStructConstraint ? ", new()" : "") + @"
{
static ref int F1()
{
var s = new T();
return ref s.P1;
}
static ref int F2()
{
var s = new T();
return ref s.P2; // 1
}
}";
comp = CreateCompilation(sourceB, references: new[] { refA });
comp.VerifyEmitDiagnostics(
// (11,20): error CS8168: Cannot return local 's' by reference because it is not a ref local
// return ref s.P2; // 1
Diagnostic(ErrorCode.ERR_RefReturnLocal, "s").WithArguments("s").WithLocation(11, 20));
}
// This is a clone of UnscopedRefAttribute_Property_02 from RefFieldTests.cs
[CombinatorialData]
[Theory]
public void UnscopedRefAttribute_Property_02_ClassConstrainedTypeParameter(bool useCompilationReference)
{
var sourceA =
@"using System.Diagnostics.CodeAnalysis;
public interface S<T>
{
public ref T P1 {get;}
[UnscopedRef] public ref T P2 {get;}
}";
var comp = CreateCompilation(new[] { sourceA, UnscopedRefAttributeDefinition });
comp.VerifyEmitDiagnostics();
var refA = AsReference(comp, useCompilationReference);
var sourceB =
@"class Program<T> where T : class, S<int>, new()
{
static ref int F1()
{
var s = new T();
return ref s.P1;
}
static ref int F2()
{
var s = new T();
return ref s.P2; // 1
}
}";
comp = CreateCompilation(sourceB, references: new[] { refA });
comp.VerifyEmitDiagnostics();
}
public enum ThreeState : byte
{
Unknown = 0,
False = 1,
True = 2,
}
// This is a clone of UnscopedRefAttribute_NestedAccess_MethodOrProperty from RefFieldTests.cs
[Theory, CombinatorialData]
public void UnscopedRefAttribute_NestedAccess_MethodOrProperty(bool firstIsMethod, bool secondIsMethod, ThreeState tS1IsClass, ThreeState tS2IsClass)
{
var source = $$"""
using System.Diagnostics.CodeAnalysis;
{{(tS1IsClass == ThreeState.True || tS2IsClass == ThreeState.True ? "" : """
var c = new C<S1<S2>, S2>();
c.Value() = 12;
System.Console.WriteLine(c.Value());
""")}}
class C<TS1, TS2>
where TS1 : {{(tS1IsClass switch { ThreeState.False => "struct, ", ThreeState.True => "class, ", _ => "" })}}IS1<TS2>
where TS2 : {{(tS2IsClass switch { ThreeState.False => "struct, ", ThreeState.True => "class, ", _ => "" })}}IS2
{
public ref int Value() => ref s1.S2{{csharp(firstIsMethod)}}.Value{{csharp(secondIsMethod)}};
#line 100
private TS1 s1;
}
struct S1<TS2> : IS1<TS2> where TS2 : IS2
{
private TS2 s2;
[UnscopedRef] public ref TS2 S2{{csharp(firstIsMethod)}} => ref s2;
}
struct S2 : IS2
{
private int value;
[UnscopedRef] public ref int Value{{csharp(secondIsMethod)}} => ref value;
}
interface IS1<TS2> where TS2 : IS2
{
[UnscopedRef] public ref TS2 S2{{(firstIsMethod ? "();" : "{get;}")}}
}
interface IS2
{
[UnscopedRef] public ref int Value{{(secondIsMethod ? "();" : "{get;}")}}
}
""";
var verifier = CompileAndVerify(new[] { source, UnscopedRefAttributeDefinition }, expectedOutput: (tS1IsClass == ThreeState.True || tS2IsClass == ThreeState.True ? null : "12"), verify: Verification.Fails);
verifier.VerifyDiagnostics(
// 0.cs(100,17): warning CS0649: Field 'C<TS1, TS2>.s1' is never assigned to, and will always have its default value
// private TS1 s1;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "s1").WithArguments("C<TS1, TS2>.s1", tS1IsClass == ThreeState.True ? "null" : "").WithLocation(100, 17)
);
verifier.VerifyMethodBody("C<TS1, TS2>.Value",
tS1IsClass == ThreeState.True ? $$"""
{
// Code size 28 (0x1c)
.maxstack 1
// sequence point: s1.S2{{csharp(firstIsMethod)}}.Value{{csharp(secondIsMethod)}}
IL_0000: ldarg.0
IL_0001: ldfld "TS1 C<TS1, TS2>.s1"
IL_0006: box "TS1"
IL_000b: callvirt "ref TS2 IS1<TS2>.S2{{il(firstIsMethod)}}"
IL_0010: constrained. "TS2"
IL_0016: callvirt "ref int IS2.Value{{il(secondIsMethod)}}"
IL_001b: ret
}
""" : $$"""
{
// Code size 29 (0x1d)
.maxstack 1
// sequence point: s1.S2{{csharp(firstIsMethod)}}.Value{{csharp(secondIsMethod)}}
IL_0000: ldarg.0
IL_0001: ldflda "TS1 C<TS1, TS2>.s1"
IL_0006: constrained. "TS1"
IL_000c: callvirt "ref TS2 IS1<TS2>.S2{{il(firstIsMethod)}}"
IL_0011: constrained. "TS2"
IL_0017: callvirt "ref int IS2.Value{{il(secondIsMethod)}}"
IL_001c: ret
}
""");
static string csharp(bool method) => method ? "()" : "";
static string il(bool method) => method ? "()" : ".get";
}
// This is a clone of UnscopedRefAttribute_NestedAccess_Properties_Invalid from RefFieldTests.cs
[Fact]
public void UnscopedRefAttribute_NestedAccess_Properties_Invalid_DirectInterface()
{
var source = """
using System.Diagnostics.CodeAnalysis;
class C
{
private S1 s1;
public ref int Value() => ref s1.S2.Value;
}
struct S1
{
private S2 s2;
public S2 S2 => s2;
}
interface S2
{
[UnscopedRef] public ref int Value {get;}
}
""";
CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }).VerifyDiagnostics(
// 0.cs(11,16): warning CS0649: Field 'S1.s2' is never assigned to, and will always have its default value null
// private S2 s2;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "s2").WithArguments("S1.s2", "null").WithLocation(11, 16));
}
// This is a clone of UnscopedRefAttribute_NestedAccess_Properties_Invalid from RefFieldTests.cs
[CombinatorialData]
[Theory]
public void UnscopedRefAttribute_NestedAccess_Properties_Invalid_ConstrainedTypeParameter(bool addStructConstraint)
{
var source =
@"using System.Diagnostics.CodeAnalysis;
class C<T> where T : " + (addStructConstraint ? "struct, " : "") + @"S2
{
private S1<T> s1;
public ref int Value() => ref s1.S2.Value;
}
struct S1<T> where T : S2
{
private T s2;
public T S2 => s2;
}
interface S2
{
[UnscopedRef] public ref int Value {get;}
}
";
CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }).VerifyDiagnostics(
// 0.cs(6,35): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// public ref int Value() => ref s1.S2.Value;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "s1.S2").WithLocation(6, 35),
// 0.cs(11,15): warning CS0649: Field 'S1<T>.s2' is never assigned to, and will always have its default value
// private T s2;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "s2").WithArguments("S1<T>.s2", "").WithLocation(11, 15));
}
// This is a clone of UnscopedRefAttribute_NestedAccess_Properties_Invalid from RefFieldTests.cs
[Fact]
public void UnscopedRefAttribute_NestedAccess_Properties_Invalid_ClassConstrainedTypeParameter()
{
var source =
@"using System.Diagnostics.CodeAnalysis;
class C<T> where T : class, S2
{
private S1<T> s1;
public ref int Value() => ref s1.S2.Value;
}
struct S1<T> where T : S2
{
private T s2;
public T S2 => s2;
}
interface S2
{
[UnscopedRef] public ref int Value {get;}
}
";
CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }).VerifyDiagnostics(
// 0.cs(11,15): warning CS0649: Field 'S1<T>.s2' is never assigned to, and will always have its default value
// private T s2;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "s2").WithArguments("S1<T>.s2", "").WithLocation(11, 15));
}
// This is a clone of UnscopedRef_ArgumentsMustMatch_01 from RefFieldTests.cs
[Fact]
public void UnscopedRef_ArgumentsMustMatch_01_DirectInterface()
{
var source = """
using System.Diagnostics.CodeAnalysis;
ref struct RefByteContainer
{
public ref byte RB;
public RefByteContainer(ref byte rb)
{
RB = ref rb;
}
}
interface ByteContainer
{
[UnscopedRef]
public RefByteContainer ByteRef {get;}
[UnscopedRef]
public RefByteContainer GetByteRef();
}
public class Program
{
static void M11(ref ByteContainer bc)
{
// ok. because ref-safe-to-escape of 'this' in 'ByteContainer.ByteRef.get' is 'ReturnOnly',
// we know that 'ref bc' will not end up written to a ref field within 'bc'.
_ = bc.ByteRef;
}
static void M12(ref ByteContainer bc)
{
// ok. because ref-safe-to-escape of 'this' in 'ByteContainer.GetByteRef()' is 'ReturnOnly',
// we know that 'ref bc' will not end up written to a ref field within 'bc'.
_ = bc.GetByteRef();
}
static void M21(ref ByteContainer bc, ref RefByteContainer rbc)
{
// error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter.
rbc = bc.ByteRef; // 1
}
static void M22(ref ByteContainer bc, ref RefByteContainer rbc)
{
// error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter.
rbc = bc.GetByteRef(); // 2
}
static RefByteContainer M31(ref ByteContainer bc)
// ok. ref-safe-to-escape of 'bc' is 'ReturnOnly'.
=> bc.ByteRef;
static RefByteContainer M32(ref ByteContainer bc)
// ok. ref-safe-to-escape of 'bc' is 'ReturnOnly'.
=> bc.GetByteRef();
static RefByteContainer M41(scoped ref ByteContainer bc)
// error: `bc.ByteRef` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod.
=> bc.ByteRef; // 3
static RefByteContainer M42(scoped ref ByteContainer bc)
// error: `bc.GetByteRef()` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod.
=> bc.GetByteRef(); // 4
}
""";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyDiagnostics();
}
// This is a clone of UnscopedRef_ArgumentsMustMatch_01 from RefFieldTests.cs
[Theory]
[CombinatorialData]
public void UnscopedRef_ArgumentsMustMatch_01_ConstrainedTypeParameter(bool addStructConstraint)
{
var source = $$"""
using System.Diagnostics.CodeAnalysis;
ref struct RefByteContainer
{
public ref byte RB;
public RefByteContainer(ref byte rb)
{
RB = ref rb;
}
}
interface ByteContainer
{
[UnscopedRef]
public RefByteContainer ByteRef {get;}
[UnscopedRef]
public RefByteContainer GetByteRef();
}
class Program<TByteContainer> where TByteContainer : {{(addStructConstraint ? "struct, " : "")}} ByteContainer
{
static void M11(ref TByteContainer bc)
{
// ok. because ref-safe-to-escape of 'this' in 'ByteContainer.ByteRef.get' is 'ReturnOnly',
// we know that 'ref bc' will not end up written to a ref field within 'bc'.
_ = bc.ByteRef;
}
static void M12(ref TByteContainer bc)
{
// ok. because ref-safe-to-escape of 'this' in 'ByteContainer.GetByteRef()' is 'ReturnOnly',
// we know that 'ref bc' will not end up written to a ref field within 'bc'.
_ = bc.GetByteRef();
}
static void M21(ref TByteContainer bc, ref RefByteContainer rbc)
{
// error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter.
rbc = bc.ByteRef; // 1
}
static void M22(ref TByteContainer bc, ref RefByteContainer rbc)
{
// error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter.
rbc = bc.GetByteRef(); // 2
}
static RefByteContainer M31(ref TByteContainer bc)
// ok. ref-safe-to-escape of 'bc' is 'ReturnOnly'.
=> bc.ByteRef;
static RefByteContainer M32(ref TByteContainer bc)
// ok. ref-safe-to-escape of 'bc' is 'ReturnOnly'.
=> bc.GetByteRef();
static RefByteContainer M41(scoped ref TByteContainer bc)
// error: `bc.ByteRef` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod.
=> bc.ByteRef; // 3
static RefByteContainer M42(scoped ref TByteContainer bc)
// error: `bc.GetByteRef()` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod.
=> bc.GetByteRef(); // 4
}
""";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyDiagnostics(
// (40,15): error CS9077: Cannot return a parameter by reference 'bc' through a ref parameter; it can only be returned in a return statement
// rbc = bc.ByteRef; // 1
Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "bc").WithArguments("bc").WithLocation(40, 15),
// (45,15): error CS9077: Cannot return a parameter by reference 'bc' through a ref parameter; it can only be returned in a return statement
// rbc = bc.GetByteRef(); // 2
Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "bc").WithArguments("bc").WithLocation(45, 15),
// (58,12): error CS9075: Cannot return a parameter by reference 'bc' because it is scoped to the current method
// => bc.ByteRef; // 3
Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "bc").WithArguments("bc").WithLocation(58, 12),
// (62,12): error CS9075: Cannot return a parameter by reference 'bc' because it is scoped to the current method
// => bc.GetByteRef(); // 4
Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "bc").WithArguments("bc").WithLocation(62, 12)
);
}
// This is a clone of UnscopedRef_ArgumentsMustMatch_01 from RefFieldTests.cs
[Fact]
public void UnscopedRef_ArgumentsMustMatch_01_ClassConstrainedTypeParameter()
{
var source = """
using System.Diagnostics.CodeAnalysis;
ref struct RefByteContainer
{
public ref byte RB;
public RefByteContainer(ref byte rb)
{
RB = ref rb;
}
}
interface ByteContainer
{
[UnscopedRef]
public RefByteContainer ByteRef {get;}
[UnscopedRef]
public RefByteContainer GetByteRef();
}
class Program<TByteContainer> where TByteContainer : class, ByteContainer
{
static void M11(ref TByteContainer bc)
{
// ok. because ref-safe-to-escape of 'this' in 'ByteContainer.ByteRef.get' is 'ReturnOnly',
// we know that 'ref bc' will not end up written to a ref field within 'bc'.
_ = bc.ByteRef;
}
static void M12(ref TByteContainer bc)
{
// ok. because ref-safe-to-escape of 'this' in 'ByteContainer.GetByteRef()' is 'ReturnOnly',
// we know that 'ref bc' will not end up written to a ref field within 'bc'.
_ = bc.GetByteRef();
}
static void M21(ref TByteContainer bc, ref RefByteContainer rbc)
{
// error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter.
rbc = bc.ByteRef; // 1
}
static void M22(ref TByteContainer bc, ref RefByteContainer rbc)
{
// error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter.
rbc = bc.GetByteRef(); // 2
}
static RefByteContainer M31(ref TByteContainer bc)
// ok. ref-safe-to-escape of 'bc' is 'ReturnOnly'.
=> bc.ByteRef;
static RefByteContainer M32(ref TByteContainer bc)
// ok. ref-safe-to-escape of 'bc' is 'ReturnOnly'.
=> bc.GetByteRef();
static RefByteContainer M41(scoped ref TByteContainer bc)
// error: `bc.ByteRef` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod.
=> bc.ByteRef; // 3
static RefByteContainer M42(scoped ref TByteContainer bc)
// error: `bc.GetByteRef()` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod.
=> bc.GetByteRef(); // 4
}
""";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyDiagnostics();
}
// This is a clone of UnscopedRef_ArgumentsMustMatch_01 from RefFieldTests.cs
[Fact]
public void UnscopedRef_ArgumentsMustMatch_01()
{
var source = """
using System.Diagnostics.CodeAnalysis;
interface IRefByteContainer
{
//public RefByteContainer(ref byte rb)
//{
// RB = ref rb;
//}
}
interface IByteContainer<RefByteContainer> where RefByteContainer : IRefByteContainer, allows ref struct
{
//public byte B;
[UnscopedRef]
public RefByteContainer ByteRef {get;}
[UnscopedRef]
public RefByteContainer GetByteRef();
}
class Program<RefByteContainer, ByteContainer> where RefByteContainer : IRefByteContainer, allows ref struct where ByteContainer : IByteContainer<RefByteContainer>, allows ref struct
{
static void M11(ref ByteContainer bc)
{
// ok. because ref-safe-to-escape of 'this' in 'ByteContainer.ByteRef.get' is 'ReturnOnly',
// we know that 'ref bc' will not end up written to a ref field within 'bc'.
_ = bc.ByteRef;
}
static void M12(ref ByteContainer bc)
{
// ok. because ref-safe-to-escape of 'this' in 'ByteContainer.GetByteRef()' is 'ReturnOnly',
// we know that 'ref bc' will not end up written to a ref field within 'bc'.
_ = bc.GetByteRef();
}
static void M21(ref ByteContainer bc, ref RefByteContainer rbc)
{
// error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter.
rbc = bc.ByteRef; // 1
}
static void M22(ref ByteContainer bc, ref RefByteContainer rbc)
{
// error. ref-safe-to-escape of 'bc' is 'ReturnOnly', therefore 'bc.ByteRef' can't be assigned to a ref parameter.
rbc = bc.GetByteRef(); // 2
}
static RefByteContainer M31(ref ByteContainer bc)
// ok. ref-safe-to-escape of 'bc' is 'ReturnOnly'.
=> bc.ByteRef;
static RefByteContainer M32(ref ByteContainer bc)
// ok. ref-safe-to-escape of 'bc' is 'ReturnOnly'.
=> bc.GetByteRef();
static RefByteContainer M41(scoped ref ByteContainer bc)
// error: `bc.ByteRef` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod.
=> bc.ByteRef; // 3
static RefByteContainer M42(scoped ref ByteContainer bc)
// error: `bc.GetByteRef()` may contain a reference to `bc`, whose ref-safe-to-escape is CurrentMethod.
=> bc.GetByteRef(); // 4
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (42,15): error CS9077: Cannot return a parameter by reference 'bc' through a ref parameter; it can only be returned in a return statement
// rbc = bc.ByteRef; // 1
Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "bc").WithArguments("bc").WithLocation(42, 15),
// (47,15): error CS9077: Cannot return a parameter by reference 'bc' through a ref parameter; it can only be returned in a return statement
// rbc = bc.GetByteRef(); // 2
Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "bc").WithArguments("bc").WithLocation(47, 15),
// (60,12): error CS9075: Cannot return a parameter by reference 'bc' because it is scoped to the current method
// => bc.ByteRef; // 3
Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "bc").WithArguments("bc").WithLocation(60, 12),
// (64,12): error CS9075: Cannot return a parameter by reference 'bc' because it is scoped to the current method
// => bc.GetByteRef(); // 4
Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "bc").WithArguments("bc").WithLocation(64, 12));
}
// This is a clone of PatternIndex_01 from RefFieldTests.cs
[Fact]
public void PatternIndex_01_DirectInterface()
{
string source = """
using System;
using System.Diagnostics.CodeAnalysis;
interface R
{
public int Length {get;}
[UnscopedRef] public ref int this[int i] {get;}
}
class Program
{
static ref int F1(ref R r1)
{
ref int i1 = ref r1[^1];
return ref i1;
}
static ref int F2(ref R r2, Index i)
{
ref int i2 = ref r2[i];
return ref i2;
}
static ref int F3()
{
R r3 = GetR();
ref int i3 = ref r3[^3];
return ref i3; // 1
}
static ref int F4(Index i)
{
R r4 = GetR();
ref int i4 = ref r4[i];
return ref i4; // 2
}
static R GetR() => null;
}
""";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyDiagnostics();
}
// This is a clone of PatternIndex_01 from RefFieldTests.cs
[Theory]
[CombinatorialData]
public void PatternIndex_01_ConstrainedTypeParameter(bool addStructConstraint)
{
string source = $$"""
using System;
using System.Diagnostics.CodeAnalysis;
interface R
{
public int Length {get;}
[UnscopedRef] public ref int this[int i] {get;}
}
class Program<TR> where TR : {{(addStructConstraint ? "struct, " : "")}} R {{(!addStructConstraint ? ", new()" : "")}}
{
static ref int F1(ref TR r1)
{
ref int i1 = ref r1[^1];
return ref i1;
}
static ref int F2(ref TR r2, Index i)
{
ref int i2 = ref r2[i];
return ref i2;
}
static ref int F3()
{
TR r3 = new TR();
ref int i3 = ref r3[^3];
return ref i3; // 1
}
static ref int F4(Index i)
{
TR r4 = new TR();
ref int i4 = ref r4[i];
return ref i4; // 2
}
}
""";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyDiagnostics(
// (24,20): error CS8157: Cannot return 'i3' by reference because it was initialized to a value that cannot be returned by reference
// return ref i3; // 1
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "i3").WithArguments("i3").WithLocation(24, 20),
// (30,20): error CS8157: Cannot return 'i4' by reference because it was initialized to a value that cannot be returned by reference
// return ref i4; // 2
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "i4").WithArguments("i4").WithLocation(30, 20));
}
// This is a clone of PatternIndex_01 from RefFieldTests.cs
[Fact]
public void PatternIndex_01_ClassConstrainedTypeParameter()
{
string source = """
using System;
using System.Diagnostics.CodeAnalysis;
interface R
{
public int Length {get;}
[UnscopedRef] public ref int this[int i] {get;}
}
class Program<TR> where TR : class, R, new()
{
static ref int F1(ref TR r1)
{
ref int i1 = ref r1[^1];
return ref i1;
}
static ref int F2(ref TR r2, Index i)
{
ref int i2 = ref r2[i];
return ref i2;
}
static ref int F3()
{
TR r3 = new TR();
ref int i3 = ref r3[^3];
return ref i3; // 1
}
static ref int F4(Index i)
{
TR r4 = new TR();
ref int i4 = ref r4[i];
return ref i4; // 2
}
}
""";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyDiagnostics();
}
// This is a clone of MemberOfReadonlyRefLikeEscape from RefEscapingTests.cs
[Fact]
public void MemberOfReadonlyRefLikeEscape_DirectInterface()
{
var text = @"
using System;
using System.Diagnostics.CodeAnalysis;
public static class Program
{
public static void Main()
{
Span<int> value1 = stackalloc int[1];
// Ok, the new value can be copied into SW but not the
// ref to the value
Get_SW().TryGet(out value1);
// Error as the ref of this can escape into value2
Span<int> value2 = default;
Get_SW().TryGet2(out value2);
}
static SW Get_SW() => throw null;
}
interface SW
{
public void TryGet(out Span<int> result);
[UnscopedRef]
public void TryGet2(out Span<int> result);
}
";
CreateCompilationWithMscorlibAndSpan(new[] { text, UnscopedRefAttributeDefinition }).VerifyDiagnostics();
}
// This is a clone of MemberOfReadonlyRefLikeEscape from RefEscapingTests.cs
[Theory]
[CombinatorialData]
public void MemberOfReadonlyRefLikeEscape_ConstrainedTypeParameter(bool addStructConstraint)
{
var text = @"
using System;
using System.Diagnostics.CodeAnalysis;
static class Program<TSW> where TSW : " + (addStructConstraint ? "struct, " : "") + @"SW" + (!addStructConstraint ? ", new()" : "") + @"
{
public static void Main()
{
Span<int> value1 = stackalloc int[1];
// Ok, the new value can be copied into SW but not the
// ref to the value
new TSW().TryGet(out value1);
// Error as the ref of this can escape into value2
Span<int> value2 = default;
new TSW().TryGet2(out value2);
}
}
interface SW
{
public void TryGet(out Span<int> result);
[UnscopedRef]
public void TryGet2(out Span<int> result);
}
";
CreateCompilationWithMscorlibAndSpan(new[] { text, UnscopedRefAttributeDefinition }).VerifyDiagnostics(
// 0.cs(16,13): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// new TSW().TryGet2(out value2);
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "new TSW()").WithLocation(16, 13),
// 0.cs(16,13): error CS8350: This combination of arguments to 'SW.TryGet2(out Span<int>)' is disallowed because it may expose variables referenced by parameter 'this' outside of their declaration scope
// new TSW().TryGet2(out value2);
Diagnostic(ErrorCode.ERR_CallArgMixing, "new TSW().TryGet2(out value2)").WithArguments("SW.TryGet2(out System.Span<int>)", "this").WithLocation(16, 13)
);
}
// This is a clone of MemberOfReadonlyRefLikeEscape from RefEscapingTests.cs
[Fact]
public void MemberOfReadonlyRefLikeEscape_ClassConstrainedTypeParameter()
{
var text = @"
using System;
using System.Diagnostics.CodeAnalysis;
static class Program<TSW> where TSW : class, SW, new()
{
public static void Main()
{
Span<int> value1 = stackalloc int[1];
// Ok, the new value can be copied into SW but not the
// ref to the value
new TSW().TryGet(out value1);
// Error as the ref of this can escape into value2
Span<int> value2 = default;
new TSW().TryGet2(out value2);
}
}
interface SW
{
public void TryGet(out Span<int> result);
[UnscopedRef]
public void TryGet2(out Span<int> result);
}
";
CreateCompilationWithMscorlibAndSpan(new[] { text, UnscopedRefAttributeDefinition }).VerifyDiagnostics();
}
// This is a clone of DefensiveCopy_01 from RefEscapingTests.cs
[Fact]
public void DefensiveCopy_01_DirectInterface()
{
var source =
@"
using System;
using System.Diagnostics.CodeAnalysis;
internal class Program
{
private static readonly Vec4 ReadOnlyVec = GetVec4();
static void Main()
{
// This refers to stack memory that has already been left out.
ref Vec4 local = ref Test1();
Console.WriteLine(local);
}
private static ref Vec4 Test1()
{
// Defensive copy occurs and it is placed in stack memory implicitly.
// The method returns a reference to the copy, which happens invalid memory access.
ref Vec4 xyzw1 = ref ReadOnlyVec.Self;
return ref xyzw1;
}
private static ref Vec4 Test2()
{
var copy = ReadOnlyVec;
ref Vec4 xyzw2 = ref copy.Self;
return ref xyzw2;
}
private static ref Vec4 Test3()
{
ref Vec4 xyzw3 = ref ReadOnlyVec.Self2();
return ref xyzw3;
}
private static ref Vec4 Test4()
{
var copy = ReadOnlyVec;
ref Vec4 xyzw4 = ref copy.Self2();
return ref xyzw4;
}
static Vec4 GetVec4() => throw null;
}
public interface Vec4
{
[UnscopedRef]
public ref Vec4 Self {get;}
[UnscopedRef]
public ref Vec4 Self2();
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
}
// This is a clone of DefensiveCopy_01 from RefEscapingTests.cs
[Theory]
[CombinatorialData]
public void DefensiveCopy_01_ConstrainedTypeParameter(bool addStructConstraint)
{
var source =
@"
using System;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
internal class Program<TVec4> where TVec4 : " + (addStructConstraint ? "struct, " : "") + @" Vec4<TVec4>
{
private static readonly TVec4 ReadOnlyVec = GetVec4();
static void Main()
{
// This refers to stack memory that has already been left out.
ref TVec4 local = ref Test1();
Console.WriteLine(local);
}
private static ref TVec4 Test1()
{
// Defensive copy occurs and it is placed in stack memory implicitly.
// The method returns a reference to the copy, which happens invalid memory access.
ref TVec4 xyzw1 = ref ReadOnlyVec.Self;
return ref xyzw1;
}
private static ref TVec4 Test2()
{
var copy = ReadOnlyVec;
ref TVec4 xyzw2 = ref copy.Self;
return ref xyzw2;
}
private static ref TVec4 Test3()
{
ref TVec4 xyzw3 = ref ReadOnlyVec.Self2();
return ref xyzw3;
}
private static ref TVec4 Test4()
{
var copy = ReadOnlyVec;
ref TVec4 xyzw4 = ref copy.Self2();
return ref xyzw4;
}
static TVec4 GetVec4() => throw null;
}
public interface Vec4<TVec4> where TVec4 : Vec4<TVec4>
{
[UnscopedRef]
public ref TVec4 Self {get;}
[UnscopedRef]
public ref TVec4 Self2();
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics(
// (22,20): error CS8157: Cannot return 'xyzw1' by reference because it was initialized to a value that cannot be returned by reference
// return ref xyzw1;
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "xyzw1").WithArguments("xyzw1").WithLocation(22, 20),
// (29,20): error CS8157: Cannot return 'xyzw2' by reference because it was initialized to a value that cannot be returned by reference
// return ref xyzw2;
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "xyzw2").WithArguments("xyzw2").WithLocation(29, 20),
// (35,20): error CS8157: Cannot return 'xyzw3' by reference because it was initialized to a value that cannot be returned by reference
// return ref xyzw3;
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "xyzw3").WithArguments("xyzw3").WithLocation(35, 20),
// (42,20): error CS8157: Cannot return 'xyzw4' by reference because it was initialized to a value that cannot be returned by reference
// return ref xyzw4;
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "xyzw4").WithArguments("xyzw4").WithLocation(42, 20)
);
}
// This is a clone of DefensiveCopy_01 from RefEscapingTests.cs
[Fact]
public void DefensiveCopy_01_ClassConstrainedTypeParameter()
{
var source =
@"
using System;
using System.Diagnostics.CodeAnalysis;
internal class Program<TVec4> where TVec4 : class, Vec4<TVec4>
{
private static readonly TVec4 ReadOnlyVec = GetVec4();
static void Main()
{
// This refers to stack memory that has already been left out.
ref TVec4 local = ref Test1();
Console.WriteLine(local);
}
private static ref TVec4 Test1()
{
// Defensive copy occurs and it is placed in stack memory implicitly.
// The method returns a reference to the copy, which happens invalid memory access.
ref TVec4 xyzw1 = ref ReadOnlyVec.Self;
return ref xyzw1;
}
private static ref TVec4 Test2()
{
var copy = ReadOnlyVec;
ref TVec4 xyzw2 = ref copy.Self;
return ref xyzw2;
}
private static ref TVec4 Test3()
{
ref TVec4 xyzw3 = ref ReadOnlyVec.Self2();
return ref xyzw3;
}
private static ref TVec4 Test4()
{
var copy = ReadOnlyVec;
ref TVec4 xyzw4 = ref copy.Self2();
return ref xyzw4;
}
static TVec4 GetVec4() => throw null;
}
public interface Vec4<TVec4> where TVec4 : Vec4<TVec4>
{
[UnscopedRef]
public ref TVec4 Self {get;}
[UnscopedRef]
public ref TVec4 Self2();
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
}
// This is a clone of DefensiveCopy_02 from RefEscapingTests.cs
[Fact]
public void DefensiveCopy_02_DirectInterface()
{
var source =
@"using System.Diagnostics.CodeAnalysis;
class Program
{
static ref Wrap m1(in Wrap i)
{
ref Wrap r1 = ref i.Self; // defensive copy
return ref r1; // ref to the local copy
}
static ref Wrap m2(in Wrap i)
{
var copy = i;
ref Wrap r2 = ref copy.Self;
return ref r2; // ref to the local copy
}
static ref Wrap m3(in Wrap i)
{
ref Wrap r3 = ref i.Self2();
return ref r3;
}
static ref Wrap m4(in Wrap i)
{
var copy = i;
ref Wrap r4 = ref copy.Self2();
return ref r4; // ref to the local copy
}
}
interface Wrap
{
[UnscopedRef]
public ref Wrap Self {get;}
[UnscopedRef]
public ref Wrap Self2();
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
}
// This is a clone of DefensiveCopy_02 from RefEscapingTests.cs
[Theory]
[CombinatorialData]
public void DefensiveCopy_02_ConstrainedTypeParameter(bool addStructConstraint)
{
var source =
@"using System.Diagnostics.CodeAnalysis;
class Program<TWrap> where TWrap : " + (addStructConstraint ? "struct, " : "") + @"Wrap<TWrap>
{
static ref TWrap m1(in TWrap i)
{
ref TWrap r1 = ref i.Self; // defensive copy
return ref r1; // ref to the local copy
}
static ref TWrap m2(in TWrap i)
{
var copy = i;
ref TWrap r2 = ref copy.Self;
return ref r2; // ref to the local copy
}
static ref TWrap m3(in TWrap i)
{
ref TWrap r3 = ref i.Self2();
return ref r3;
}
static ref TWrap m4(in TWrap i)
{
var copy = i;
ref TWrap r4 = ref copy.Self2();
return ref r4; // ref to the local copy
}
}
interface Wrap<T> where T : Wrap<T>
{
[UnscopedRef]
public ref T Self {get;}
[UnscopedRef]
public ref T Self2();
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics(
// (8,20): error CS8157: Cannot return 'r1' by reference because it was initialized to a value that cannot be returned by reference
// return ref r1; // ref to the local copy
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "r1").WithArguments("r1").WithLocation(8, 20),
// (15,20): error CS8157: Cannot return 'r2' by reference because it was initialized to a value that cannot be returned by reference
// return ref r2; // ref to the local copy
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "r2").WithArguments("r2").WithLocation(15, 20),
// (21,20): error CS8157: Cannot return 'r3' by reference because it was initialized to a value that cannot be returned by reference
// return ref r3;
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "r3").WithArguments("r3").WithLocation(21, 20),
// (28,20): error CS8157: Cannot return 'r4' by reference because it was initialized to a value that cannot be returned by reference
// return ref r4; // ref to the local copy
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "r4").WithArguments("r4").WithLocation(28, 20)
);
}
// This is a clone of DefensiveCopy_02 from RefEscapingTests.cs
[Fact]
public void DefensiveCopy_02_ClassConstrainedTypeParameter()
{
var source =
@"using System.Diagnostics.CodeAnalysis;
class Program<TWrap> where TWrap : class, Wrap<TWrap>
{
static ref TWrap m1(in TWrap i)
{
ref TWrap r1 = ref i.Self; // defensive copy
return ref r1; // ref to the local copy
}
static ref TWrap m2(in TWrap i)
{
var copy = i;
ref TWrap r2 = ref copy.Self;
return ref r2; // ref to the local copy
}
static ref TWrap m3(in TWrap i)
{
ref TWrap r3 = ref i.Self2();
return ref r3;
}
static ref TWrap m4(in TWrap i)
{
var copy = i;
ref TWrap r4 = ref copy.Self2();
return ref r4; // ref to the local copy
}
}
interface Wrap<T> where T : Wrap<T>
{
[UnscopedRef]
public ref T Self {get;}
[UnscopedRef]
public ref T Self2();
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
}
// This is a clone of DefensiveCopy_05 from RefEscapingTests.cs
[Fact]
public void DefensiveCopy_05_DirectInterface()
{
var source =
@"
using System;
using System.Diagnostics.CodeAnalysis;
internal class Program
{
private static readonly Vec4 ReadOnlyVec = default;
static void Main()
{
}
private static Span<float> Test1()
{
var xyzw1 = ReadOnlyVec.Self;
return xyzw1;
}
private static Span<float> Test2()
{
var r2 = ReadOnlyVec;
var xyzw2 = r2.Self;
return xyzw2;
}
}
public interface Vec4
{
[UnscopedRef]
public Span<float> Self
{ get; set; }
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
}
// This is a clone of DefensiveCopy_05 from RefEscapingTests.cs
[Theory]
[CombinatorialData]
public void DefensiveCopy_05_ConstrainedTypeParameter(bool addStructConstraint)
{
var source =
@"
using System;
using System.Diagnostics.CodeAnalysis;
internal class Program<TVec4> where TVec4 : " + (addStructConstraint ? "struct, " : "") + @" Vec4
{
private static readonly TVec4 ReadOnlyVec = default;
static void Main()
{
}
private static Span<float> Test1()
{
var xyzw1 = ReadOnlyVec.Self;
return xyzw1;
}
private static Span<float> Test2()
{
var r2 = ReadOnlyVec;
var xyzw2 = r2.Self;
return xyzw2;
}
}
public interface Vec4
{
[UnscopedRef]
public Span<float> Self
{ get; set; }
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics(
// (16,16): error CS8352: Cannot use variable 'xyzw1' in this context because it may expose referenced variables outside of their declaration scope
// return xyzw1;
Diagnostic(ErrorCode.ERR_EscapeVariable, "xyzw1").WithArguments("xyzw1").WithLocation(16, 16),
// (23,16): error CS8352: Cannot use variable 'xyzw2' in this context because it may expose referenced variables outside of their declaration scope
// return xyzw2;
Diagnostic(ErrorCode.ERR_EscapeVariable, "xyzw2").WithArguments("xyzw2").WithLocation(23, 16)
);
}
// This is a clone of DefensiveCopy_05 from RefEscapingTests.cs
[Fact]
public void DefensiveCopy_05_ClassConstrainedTypeParameter()
{
var source =
@"
using System;
using System.Diagnostics.CodeAnalysis;
internal class Program<TVec4> where TVec4 : class, Vec4
{
private static readonly TVec4 ReadOnlyVec = default;
static void Main()
{
}
private static Span<float> Test1()
{
var xyzw1 = ReadOnlyVec.Self;
return xyzw1;
}
private static Span<float> Test2()
{
var r2 = ReadOnlyVec;
var xyzw2 = r2.Self;
return xyzw2;
}
}
public interface Vec4
{
[UnscopedRef]
public Span<float> Self
{ get; set; }
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
}
// This is a clone of DefensiveCopy_21 from RefEscapingTests.cs
[Fact]
public void DefensiveCopy_21_DirectInterface()
{
var source =
@"
using System;
using System.Diagnostics.CodeAnalysis;
internal class Program
{
private static readonly Vec4 ReadOnlyVec = default;
static void Main()
{
}
private static Span<float> Test1()
{
var (xyzw1, _) = ReadOnlyVec;
return xyzw1;
}
private static Span<float> Test2()
{
var r2 = ReadOnlyVec;
var (xyzw2, _) = r2;
return xyzw2;
}
private static Span<float> Test3()
{
ReadOnlyVec.Deconstruct(out var xyzw3, out _);
return xyzw3;
}
private static Span<float> Test4()
{
var r4 = ReadOnlyVec;
r4.Deconstruct(out var xyzw4, out _);
return xyzw4;
}
}
public interface Vec4
{
[UnscopedRef]
public void Deconstruct(out Span<float> x, out int i);
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
}
// This is a clone of DefensiveCopy_21 from RefEscapingTests.cs
[Theory]
[CombinatorialData]
public void DefensiveCopy_21_ConstrainedTypeParameter(bool addStructConstraint)
{
var source =
@"
using System;
using System.Diagnostics.CodeAnalysis;
internal class Program<TVec4> where TVec4 : " + (addStructConstraint ? "struct, " : "") + @" Vec4
{
private static readonly TVec4 ReadOnlyVec = default;
static void Main()
{
}
private static Span<float> Test1()
{
var (xyzw1, _) = ReadOnlyVec;
return xyzw1;
}
private static Span<float> Test2()
{
var r2 = ReadOnlyVec;
var (xyzw2, _) = r2;
return xyzw2;
}
private static Span<float> Test3()
{
ReadOnlyVec.Deconstruct(out var xyzw3, out _);
return xyzw3;
}
private static Span<float> Test4()
{
var r4 = ReadOnlyVec;
r4.Deconstruct(out var xyzw4, out _);
return xyzw4;
}
}
public interface Vec4
{
[UnscopedRef]
public void Deconstruct(out Span<float> x, out int i);
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics(
// (16,16): error CS8352: Cannot use variable 'xyzw1' in this context because it may expose referenced variables outside of their declaration scope
// return xyzw1;
Diagnostic(ErrorCode.ERR_EscapeVariable, "xyzw1").WithArguments("xyzw1").WithLocation(16, 16),
// (23,16): error CS8352: Cannot use variable 'xyzw2' in this context because it may expose referenced variables outside of their declaration scope
// return xyzw2;
Diagnostic(ErrorCode.ERR_EscapeVariable, "xyzw2").WithArguments("xyzw2").WithLocation(23, 16),
// (29,16): error CS8352: Cannot use variable 'xyzw3' in this context because it may expose referenced variables outside of their declaration scope
// return xyzw3;
Diagnostic(ErrorCode.ERR_EscapeVariable, "xyzw3").WithArguments("xyzw3").WithLocation(29, 16),
// (36,16): error CS8352: Cannot use variable 'xyzw4' in this context because it may expose referenced variables outside of their declaration scope
// return xyzw4;
Diagnostic(ErrorCode.ERR_EscapeVariable, "xyzw4").WithArguments("xyzw4").WithLocation(36, 16)
);
}
// This is a clone of DefensiveCopy_21 from RefEscapingTests.cs
[Fact]
public void DefensiveCopy_21_ClassConstrainedTypeParameter()
{
var source =
@"
using System;
using System.Diagnostics.CodeAnalysis;
internal class Program<TVec4> where TVec4 : class, Vec4
{
private static readonly TVec4 ReadOnlyVec = default;
static void Main()
{
}
private static Span<float> Test1()
{
var (xyzw1, _) = ReadOnlyVec;
return xyzw1;
}
private static Span<float> Test2()
{
var r2 = ReadOnlyVec;
var (xyzw2, _) = r2;
return xyzw2;
}
private static Span<float> Test3()
{
ReadOnlyVec.Deconstruct(out var xyzw3, out _);
return xyzw3;
}
private static Span<float> Test4()
{
var r4 = ReadOnlyVec;
r4.Deconstruct(out var xyzw4, out _);
return xyzw4;
}
}
public interface Vec4
{
[UnscopedRef]
public void Deconstruct(out Span<float> x, out int i);
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void AllowsConstraint_01_SimpleTypeTypeParameter()
{
var src = @"
public class C<T>
where T : allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped,
emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true)).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
var c = m.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
Assert.True(t.GetPublicSymbol().AllowsRefLikeType);
AssertEx.Equal("C<T> where T : allows ref struct", t.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
}
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
Assert.True(comp.SupportsRuntimeCapability(RuntimeCapability.ByRefLikeGenerics));
CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (3,22): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where T : allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(3, 22)
);
comp = CreateCompilation(src, targetFramework: TargetFramework.Mscorlib461Extended, parseOptions: TestOptions.Regular13).VerifyDiagnostics(
// (3,22): error CS9240: Target runtime doesn't support by-ref-like generics.
// where T : allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(3, 22)
);
Assert.False(comp.SupportsRuntimeCapability(RuntimeCapability.ByRefLikeGenerics));
comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics(
// (3,22): error CS9240: Target runtime doesn't support by-ref-like generics.
// where T : allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(3, 22)
);
Assert.False(comp.SupportsRuntimeCapability(RuntimeCapability.ByRefLikeGenerics));
}
[Fact]
public void AllowsConstraint_02_SimpleMethodTypeParameter()
{
var src = @"
public class C
{
public void M<T>()
where T : allows ref struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
var method = m.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (5,26): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where T : allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(5, 26)
);
CreateCompilation(src, targetFramework: TargetFramework.Mscorlib461Extended, parseOptions: TestOptions.Regular13).VerifyDiagnostics(
// (5,26): error CS9240: Target runtime doesn't support by-ref-like generics.
// where T : allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(5, 26)
);
}
[Fact]
public void AllowsConstraint_03_TwoRefStructInARow()
{
var src = @"
public class C<T>
where T : allows ref struct, ref struct
{
}
public class D<T>
where T : allows ref struct, ref
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,34): error CS9241: 'ref struct' is already specified.
// where T : allows ref struct, ref struct
Diagnostic(ErrorCode.ERR_RefStructConstraintAlreadySpecified, "ref struct").WithLocation(3, 34),
// (8,34): error CS9241: 'ref struct' is already specified.
// where T : allows ref struct, ref
Diagnostic(ErrorCode.ERR_RefStructConstraintAlreadySpecified, @"ref
").WithLocation(8, 34),
// (8,37): error CS1003: Syntax error, 'struct' expected
// where T : allows ref struct, ref
Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("struct").WithLocation(8, 37)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
var d = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("D");
var dt = d.TypeParameters.Single();
Assert.False(dt.HasReferenceTypeConstraint);
Assert.False(dt.HasValueTypeConstraint);
Assert.False(dt.HasUnmanagedTypeConstraint);
Assert.False(dt.HasNotNullConstraint);
Assert.True(dt.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_04_TwoAllows()
{
var src = @"
public class C<T>
where T : allows ref struct, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, allows ref struct
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(3, 15)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var ct = c.TypeParameters.Single();
Assert.False(ct.HasReferenceTypeConstraint);
Assert.False(ct.HasValueTypeConstraint);
Assert.False(ct.HasUnmanagedTypeConstraint);
Assert.False(ct.HasNotNullConstraint);
Assert.True(ct.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_05_FollowedByStruct()
{
var src = @"
public class C<T>
where T : allows ref struct, struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, struct
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(3, 15),
// (3,34): error CS0449: The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints cannot be combined or duplicated, and must be specified first in the constraints list.
// where T : allows ref struct, struct
Diagnostic(ErrorCode.ERR_TypeConstraintsMustBeUniqueAndFirst, "struct").WithLocation(3, 34)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.True(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_06_AfterStruct()
{
var src = @"
public class C<T>
where T : struct, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.True(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_07_FollowedByClass()
{
var src = @"
public class C<T>
where T : allows ref struct, class
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (2,16): error CS9243: Cannot allow ref structs for a type parameter known from other constraints to be a class
// public class C<T>
Diagnostic(ErrorCode.ERR_ClassIsCombinedWithRefStruct, "T").WithLocation(2, 16),
// (3,15): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, class
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(3, 15),
// (3,34): error CS0449: The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints cannot be combined or duplicated, and must be specified first in the constraints list.
// where T : allows ref struct, class
Diagnostic(ErrorCode.ERR_TypeConstraintsMustBeUniqueAndFirst, "class").WithLocation(3, 34)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.True(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_08_AfterClass()
{
var src = @"
public class C<T>
where T : class, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (2,16): error CS9243: Cannot allow ref structs for a type parameter known from other constraints to be a class
// public class C<T>
Diagnostic(ErrorCode.ERR_ClassIsCombinedWithRefStruct, "T").WithLocation(2, 16)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.True(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_09_FollowedByDefault()
{
var src = @"
public class C<T>
where T : allows ref struct, default
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, default
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(3, 15),
// (3,34): error CS8823: The 'default' constraint is valid on override and explicit interface implementation methods only.
// where T : allows ref struct, default
Diagnostic(ErrorCode.ERR_DefaultConstraintOverrideOnly, "default").WithLocation(3, 34),
// (3,34): error CS0449: The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints cannot be combined or duplicated, and must be specified first in the constraints list.
// where T : allows ref struct, default
Diagnostic(ErrorCode.ERR_TypeConstraintsMustBeUniqueAndFirst, "default").WithLocation(3, 34)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_10_FollowedByDefault()
{
var src = @"
public class C
{
public void M<T>()
where T : allows ref struct, default
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (5,19): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, default
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(5, 19),
// (5,38): error CS8823: The 'default' constraint is valid on override and explicit interface implementation methods only.
// where T : allows ref struct, default
Diagnostic(ErrorCode.ERR_DefaultConstraintOverrideOnly, "default").WithLocation(5, 38),
// (5,38): error CS0449: The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints cannot be combined or duplicated, and must be specified first in the constraints list.
// where T : allows ref struct, default
Diagnostic(ErrorCode.ERR_TypeConstraintsMustBeUniqueAndFirst, "default").WithLocation(5, 38)
);
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_11_FollowedByDefault()
{
var src = @"
public class C : B
{
public override void M<T>()
where T : allows ref struct, default
{
}
}
public class B
{
public virtual void M<T>()
where T : allows ref struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (5,19): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly, except for either a 'class', or a 'struct' constraint.
// where T : allows ref struct, default
Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "allows ref struct").WithLocation(5, 19)
);
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_12_AfterDefault()
{
var src = @"
public class C<T>
where T : default, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS8823: The 'default' constraint is valid on override and explicit interface implementation methods only.
// where T : default, allows ref struct
Diagnostic(ErrorCode.ERR_DefaultConstraintOverrideOnly, "default").WithLocation(3, 15)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_13_AfterDefault()
{
var src = @"
public class C
{
public void M<T>()
where T : default, allows ref struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (5,19): error CS8823: The 'default' constraint is valid on override and explicit interface implementation methods only.
// where T : default, allows ref struct
Diagnostic(ErrorCode.ERR_DefaultConstraintOverrideOnly, "default").WithLocation(5, 19)
);
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_14_AfterDefault()
{
var src = @"
public class C : B
{
public override void M<T>()
where T : default, allows ref struct
{
}
}
public class B
{
public virtual void M<T>()
where T : allows ref struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (5,28): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly, except for either a 'class', or a 'struct' constraint.
// where T : default, allows ref struct
Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "allows ref struct").WithLocation(5, 28)
);
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_15_FollowedByUnmanaged()
{
var src = @"
public class C<T>
where T : allows ref struct, unmanaged
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, unmanaged
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(3, 15),
// (3,34): error CS0449: The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints cannot be combined or duplicated, and must be specified first in the constraints list.
// where T : allows ref struct, unmanaged
Diagnostic(ErrorCode.ERR_TypeConstraintsMustBeUniqueAndFirst, "unmanaged").WithLocation(3, 34)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_16_AfterUnmanaged()
{
var src = @"
public class C<T>
where T : unmanaged, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.True(t.HasValueTypeConstraint);
Assert.True(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_17_FollowedByNotNull()
{
var src = @"
public class C<T>
where T : allows ref struct, notnull
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, notnull
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(3, 15),
// (3,34): error CS0449: The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints cannot be combined or duplicated, and must be specified first in the constraints list.
// where T : allows ref struct, notnull
Diagnostic(ErrorCode.ERR_TypeConstraintsMustBeUniqueAndFirst, "notnull").WithLocation(3, 34)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.True(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_18_AfterNotNull()
{
var src = @"
public class C<T>
where T : notnull, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.True(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_19_FollowedByType()
{
var src = @"
public class C<T>
where T : allows ref struct, I1
{
}
public interface I1 {}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, notnull
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(3, 15)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.Equal("I1", t.ConstraintTypesNoUseSiteDiagnostics.Single().ToTestDisplayString());
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_20_AfterType()
{
var src = @"
public class C<T>
where T : I1, allows ref struct
{
}
public interface I1 {}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.Equal("I1", t.ConstraintTypesNoUseSiteDiagnostics.Single().ToTestDisplayString());
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_21_AfterClassType()
{
var src = @"
public class C<T>
where T : C1, allows ref struct
{
}
public class C1 {}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (2,16): error CS9243: Cannot allow ref structs for a type parameter known from other constraints to be a class
// public class C<T>
Diagnostic(ErrorCode.ERR_ClassIsCombinedWithRefStruct, "T").WithLocation(2, 16)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.Equal("C1", t.ConstraintTypesNoUseSiteDiagnostics.Single().ToTestDisplayString());
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_22_AfterSystemValueType()
{
var src = @"
public class C<T>
where T : System.ValueType, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS0702: Constraint cannot be special class 'ValueType'
// where T : System.ValueType, allows ref struct
Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "System.ValueType").WithArguments("System.ValueType").WithLocation(3, 15)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.Empty(t.ConstraintTypesNoUseSiteDiagnostics);
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_23_AfterSystemEnum()
{
var src = @"
public class C<T>
where T : System.Enum, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.Equal("System.Enum", t.ConstraintTypesNoUseSiteDiagnostics.Single().ToTestDisplayString());
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_24_FollowedByNew()
{
var src = @"
public class C<T>
where T : allows ref struct, new()
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,15): error CS9242: The 'allows' constraint clause must be the last constraint specified
// where T : allows ref struct, new()
Diagnostic(ErrorCode.ERR_AllowsClauseMustBeLast, "allows").WithLocation(3, 15)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.HasConstructorConstraint);
Assert.True(t.AllowsRefLikeType);
AssertEx.Equal("C<T> where T : new(), allows ref struct", t.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
}
[Fact]
public void AllowsConstraint_25_AfterNew()
{
var src = @"
public class C<T>
where T : new(), allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.HasConstructorConstraint);
Assert.True(t.AllowsRefLikeType);
AssertEx.Equal("C<T> where T : new(), allows ref struct", t.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
}
[Fact]
public void AllowsConstraint_26_PartialTypes()
{
var src = @"
partial class C<T> where T : allows ref struct
{
}
partial class C<T> where T : allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_27_PartialTypes()
{
var src = @"
partial class C<T>
{
}
partial class C<T> where T : allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_28_PartialTypes()
{
var src = @"
partial class C<T> where T : allows ref struct
{
}
partial class C<T>
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_29_PartialTypes()
{
var src = @"
partial class C<T> where T : struct
{
}
partial class C<T> where T : struct, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (2,15): error CS0265: Partial declarations of 'C<T>' have inconsistent constraints for type parameter 'T'
// partial class C<T> where T : struct
Diagnostic(ErrorCode.ERR_PartialWrongConstraints, "C").WithArguments("C<T>", "T").WithLocation(2, 15)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_30_PartialTypes()
{
var src = @"
partial class C<T> where T : struct, allows ref struct
{
}
partial class C<T> where T : struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (2,15): error CS0265: Partial declarations of 'C<T>' have inconsistent constraints for type parameter 'T'
// partial class C<T> where T : struct, allows ref struct
Diagnostic(ErrorCode.ERR_PartialWrongConstraints, "C").WithArguments("C<T>", "T").WithLocation(2, 15)
);
var c = comp.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_31_PartialMethod()
{
var src = @"
partial class C
{
partial void M<T>() where T : allows ref struct;
}
partial class C
{
partial void M<T>() where T : allows ref struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_32_PartialMethod()
{
var src = @"
partial class C
{
partial void M<T>();
}
partial class C
{
partial void M<T>() where T : allows ref struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (9,18): error CS0761: Partial method declarations of 'C.M<T>()' have inconsistent constraints for type parameter 'T'
// partial void M<T>() where T : allows ref struct
Diagnostic(ErrorCode.ERR_PartialMethodInconsistentConstraints, "M").WithArguments("C.M<T>()", "T").WithLocation(9, 18)
);
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.False(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_33_PartialMethod()
{
var src = @"
partial class C
{
partial void M<T>() where T : allows ref struct;
}
partial class C
{
partial void M<T>()
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (9,18): error CS0761: Partial method declarations of 'C.M<T>()' have inconsistent constraints for type parameter 'T'
// partial void M<T>()
Diagnostic(ErrorCode.ERR_PartialMethodInconsistentConstraints, "M").WithArguments("C.M<T>()", "T").WithLocation(9, 18)
);
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_34_PartialMethod()
{
var src = @"
partial class C
{
partial void M<T>() where T : struct;
}
partial class C
{
partial void M<T>() where T : struct, allows ref struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (9,18): error CS0761: Partial method declarations of 'C.M<T>()' have inconsistent constraints for type parameter 'T'
// partial void M<T>() where T : struct, allows ref struct
Diagnostic(ErrorCode.ERR_PartialMethodInconsistentConstraints, "M").WithArguments("C.M<T>()", "T").WithLocation(9, 18)
);
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.False(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_35_PartialMethod()
{
var src = @"
partial class C
{
partial void M<T>() where T : struct, allows ref struct;
}
partial class C
{
partial void M<T>() where T : struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (9,18): error CS0761: Partial method declarations of 'C.M<T>()' have inconsistent constraints for type parameter 'T'
// partial void M<T>() where T : struct
Diagnostic(ErrorCode.ERR_PartialMethodInconsistentConstraints, "M").WithArguments("C.M<T>()", "T").WithLocation(9, 18)
);
var method = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
var t = method.TypeParameters.Single();
Assert.True(t.AllowsRefLikeType);
}
[Fact]
public void AllowsConstraint_36_InheritedByOverride()
{
var src = @"
class C1
{
public virtual void M1<T>() where T : allows ref struct
{
}
public virtual void M2<T>() where T : unmanaged
{
}
}
class C2 : C1
{
public override void M1<T>() where T : allows ref struct
{
}
}
class C3 : C1
{
public override void M2<T>() where T : unmanaged
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (14,44): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly, except for either a 'class', or a 'struct' constraint.
// public override void M1<T>() where T : allows ref struct
Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "allows ref struct").WithLocation(14, 44),
// (21,44): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly, except for either a 'class', or a 'struct' constraint.
// public override void M2<T>() where T : unmanaged
Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "unmanaged").WithLocation(21, 44)
);
var method1 = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.M1");
var t1 = method1.TypeParameters.Single();
Assert.True(t1.AllowsRefLikeType);
var method2 = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C3.M2");
var t2 = method2.TypeParameters.Single();
Assert.True(t2.HasUnmanagedTypeConstraint);
}
[Fact]
public void AllowsConstraint_37_InheritedByOverride()
{
var src1 = @"
public class C1
{
public virtual void M1<T>() where T : allows ref struct
{
}
public virtual void M2<T>() where T : unmanaged
{
}
}
";
var src2 = @"
class C2 : C1
{
public override void M1<T>()
{
}
public override void M2<T>()
{
}
}
";
var comp1 = CreateCompilation([src1, src2], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp1.VerifyDiagnostics();
var method1 = comp1.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.M1");
var t1 = method1.TypeParameters.Single();
Assert.True(t1.AllowsRefLikeType);
var method2 = comp1.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.M2");
var t2 = method2.TypeParameters.Single();
Assert.True(t2.HasUnmanagedTypeConstraint);
CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (4,29): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public override void M1<T>()
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "T").WithArguments("allows ref struct constraint", "13.0").WithLocation(4, 29)
);
var comp2 = CreateCompilation(src1, targetFramework: TargetFramework.Net70);
CreateCompilation(src2, references: [comp2.ToMetadataReference()], targetFramework: TargetFramework.Net70).VerifyDiagnostics(
// (4,29): error CS9240: Target runtime doesn't support by-ref-like generics.
// public override void M1<T>()
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "T").WithLocation(4, 29)
);
}
[Fact]
public void AllowsConstraint_38_InheritedByOverride()
{
var src = @"
class C1<S>
{
public virtual void M1<T>() where T : S, allows ref struct
{
}
public virtual void M2<T>() where T : class, S
{
}
}
class C2 : C1<C>
{
public override void M1<T>()
{
}
public override void M2<T>()
{
}
}
class C {}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var method1 = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.M1");
var t1 = method1.TypeParameters.Single();
Assert.True(t1.AllowsRefLikeType);
Assert.Equal("C", t1.ConstraintTypesNoUseSiteDiagnostics.Single().ToTestDisplayString());
var method2 = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.M2");
var t2 = method2.TypeParameters.Single();
Assert.True(t2.HasReferenceTypeConstraint);
Assert.Equal("C", t2.ConstraintTypesNoUseSiteDiagnostics.Single().ToTestDisplayString());
}
[Fact]
public void AllowsConstraint_39_InheritedByExplicitImplementation()
{
var src = @"
interface C1
{
void M1<T>() where T : allows ref struct;
void M2<T>() where T : unmanaged;
}
class C2 : C1
{
void C1.M1<T>() where T : allows ref struct
{
}
void C1.M2<T>() where T : unmanaged
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,31): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly, except for either a 'class', or a 'struct' constraint.
// void C1.M1<T>() where T : allows ref struct
Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "allows ref struct").WithLocation(10, 31),
// (14,31): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly, except for either a 'class', or a 'struct' constraint.
// void C1.M2<T>() where T : unmanaged
Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "unmanaged").WithLocation(14, 31)
);
var method1 = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.C1.M1");
var t1 = method1.TypeParameters.Single();
Assert.True(t1.AllowsRefLikeType);
var method2 = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.C1.M2");
var t2 = method2.TypeParameters.Single();
Assert.True(t2.HasUnmanagedTypeConstraint);
}
[Fact]
public void AllowsConstraint_40_InheritedByExplicitImplementation()
{
var src1 = @"
public interface C1
{
void M1<T>() where T : allows ref struct;
void M2<T>() where T : unmanaged;
}
";
var src2 = @"
class C2 : C1
{
void C1.M1<T>()
{
}
void C1.M2<T>()
{
}
}
";
var comp1 = CreateCompilation([src1, src2], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp1.VerifyDiagnostics();
var method1 = comp1.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.C1.M1");
var t1 = method1.TypeParameters.Single();
Assert.True(t1.AllowsRefLikeType);
var method2 = comp1.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.C1.M2");
var t2 = method2.TypeParameters.Single();
Assert.True(t2.HasUnmanagedTypeConstraint);
CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13).VerifyDiagnostics();
CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (4,16): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// void C1.M1<T>()
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "T").WithArguments("allows ref struct constraint", "13.0").WithLocation(4, 16)
);
var comp2 = CreateCompilation(src1, targetFramework: TargetFramework.Net70);
CreateCompilation(src2, references: [comp2.ToMetadataReference()], targetFramework: TargetFramework.Net70).VerifyDiagnostics(
// (4,16): error CS9240: Target runtime doesn't support by-ref-like generics.
// void C1.M1<T>()
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "T").WithLocation(4, 16)
);
}
[Fact]
public void AllowsConstraint_41_InheritedByExplicitImplementation()
{
var src = @"
interface C1<S>
{
void M1<T>() where T : S, allows ref struct;
void M2<T>() where T : class, S;
}
class C2 : C1<C>
{
void C1<C>.M1<T>()
{
}
void C1<C>.M2<T>()
{
}
}
class C {}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
var method1 = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.C1<C>.M1");
var t1 = method1.TypeParameters.Single();
Assert.True(t1.AllowsRefLikeType);
Assert.Equal("C", t1.ConstraintTypesNoUseSiteDiagnostics.Single().ToTestDisplayString());
var method2 = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C2.C1<C>.M2");
var t2 = method2.TypeParameters.Single();
Assert.True(t2.HasReferenceTypeConstraint);
Assert.Equal("C", t2.ConstraintTypesNoUseSiteDiagnostics.Single().ToTestDisplayString());
}
[Fact]
public void AllowsConstraint_42_ImplicitImplementationMustMatch()
{
var src = @"
interface C1
{
void M1<T>() where T : allows ref struct;
void M2<T>() where T : unmanaged;
}
class C2 : C1
{
public void M1<T>() where T : allows ref struct
{
}
public void M2<T>() where T : unmanaged
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
}
[Fact]
public void AllowsConstraint_43_ImplicitImplementationMustMatch()
{
var src = @"
interface C1
{
void M1<T>() where T : allows ref struct;
void M2<T>() where T : unmanaged;
}
class C2 : C1
{
public void M1<T>()
{
}
public void M2<T>()
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,17): error CS0425: The constraints for type parameter 'T' of method 'C2.M1<T>()' must match the constraints for type parameter 'T' of interface method 'C1.M1<T>()'. Consider using an explicit interface implementation instead.
// public void M1<T>()
Diagnostic(ErrorCode.ERR_ImplBadConstraints, "M1").WithArguments("T", "C2.M1<T>()", "T", "C1.M1<T>()").WithLocation(10, 17),
// (13,17): error CS0425: The constraints for type parameter 'T' of method 'C2.M2<T>()' must match the constraints for type parameter 'T' of interface method 'C1.M2<T>()'. Consider using an explicit interface implementation instead.
// public void M2<T>()
Diagnostic(ErrorCode.ERR_ImplBadConstraints, "M2").WithArguments("T", "C2.M2<T>()", "T", "C1.M2<T>()").WithLocation(13, 17)
);
}
[Fact]
public void AllowsConstraint_44_ImplicitImplementationMustMatch()
{
var src = @"
interface C1<S>
{
void M1<T>() where T : S, allows ref struct;
void M2<T>() where T : class, S;
}
class C2 : C1<C>
{
public void M1<T>() where T : C, allows ref struct
{
}
public void M2<T>() where T : class, C
{
}
}
class C {}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,20): error CS9243: Cannot allow ref structs for a type parameter known from other constraints to be a class
// public void M1<T>() where T : C, allows ref struct
Diagnostic(ErrorCode.ERR_ClassIsCombinedWithRefStruct, "T").WithLocation(10, 20),
// (13,17): error CS0425: The constraints for type parameter 'T' of method 'C2.M2<T>()' must match the constraints for type parameter 'T' of interface method 'C1<C>.M2<T>()'. Consider using an explicit interface implementation instead.
// public void M2<T>() where T : class, C
Diagnostic(ErrorCode.ERR_ImplBadConstraints, "M2").WithArguments("T", "C2.M2<T>()", "T", "C1<C>.M2<T>()").WithLocation(13, 17),
// (13,42): error CS0450: 'C': cannot specify both a constraint class and the 'class' or 'struct' constraint
// public void M2<T>() where T : class, C
Diagnostic(ErrorCode.ERR_RefValBoundWithClass, "C").WithArguments("C").WithLocation(13, 42)
);
}
[Fact]
public void AllowsConstraint_45_NotPresent()
{
var src = @"
public class C<T>
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
var c = m.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters.Single();
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.False(t.AllowsRefLikeType);
Assert.False(t.GetPublicSymbol().AllowsRefLikeType);
AssertEx.Equal("C<T>", t.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
}
}
[Fact]
public void AllowsConstraint_46()
{
var src = @"
class C<T, U>
where T : allows ref struct
where U : T, allows ref struct
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
var c = m.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters[0];
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
var u = c.TypeParameters[1];
Assert.False(u.HasReferenceTypeConstraint);
Assert.False(u.HasValueTypeConstraint);
Assert.False(u.HasUnmanagedTypeConstraint);
Assert.False(u.HasNotNullConstraint);
Assert.True(u.AllowsRefLikeType);
}
}
[Fact]
public void AllowsConstraint_47()
{
var src = @"
class C<T, U>
where T : allows ref struct
where U : T
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
var c = m.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var t = c.TypeParameters[0];
Assert.False(t.HasReferenceTypeConstraint);
Assert.False(t.HasValueTypeConstraint);
Assert.False(t.HasUnmanagedTypeConstraint);
Assert.False(t.HasNotNullConstraint);
Assert.True(t.AllowsRefLikeType);
var u = c.TypeParameters[1];
Assert.False(u.HasReferenceTypeConstraint);
Assert.False(u.HasValueTypeConstraint);
Assert.False(u.HasUnmanagedTypeConstraint);
Assert.False(u.HasNotNullConstraint);
Assert.False(u.AllowsRefLikeType);
}
}
[Fact]
public void ImplementAnInterface_01()
{
var src = @"
interface I1
{}
ref struct S1 : I1
{}
struct S2 : I1
{}
";
var comp = CreateCompilation(src);
CompileAndVerify(comp, sourceSymbolValidator: verify, symbolValidator: verify).VerifyDiagnostics();
void verify(ModuleSymbol m)
{
var s1 = m.GlobalNamespace.GetMember<NamedTypeSymbol>("S1");
Assert.Equal("I1", s1.InterfacesNoUseSiteDiagnostics().Single().ToTestDisplayString());
}
CreateCompilation(src, targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation(src, targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (5,17): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// ref struct S1 : I1
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "I1").WithArguments("ref struct interfaces", "13.0").WithLocation(5, 17)
);
}
[Fact]
public void ImplementAnInterface_02_IllegalBoxing()
{
var src = @"
interface I1
{}
ref struct S1 : I1
{}
class C
{
static I1 Test1(S1 x) => x;
static I1 Test2(S1 x) => (I1)x;
}
";
var comp = CreateCompilation(src);
comp.VerifyDiagnostics(
// (10,30): error CS0029: Cannot implicitly convert type 'S1' to 'I1'
// static I1 Test1(S1 x) => x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("S1", "I1").WithLocation(10, 30),
// (11,30): error CS0030: Cannot convert type 'S1' to 'I1'
// static I1 Test2(S1 x) => (I1)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(I1)x").WithArguments("S1", "I1").WithLocation(11, 30)
);
}
[Fact]
public void ImplementAnInterface_03()
{
var src = @"
interface I1
{
void M1();
}
ref struct S1 : I1
{
public void M1()
{
System.Console.Write(""S1.M1"");
}
}
class C
{
static void Main()
{
Test(new S1());
}
static void Test2<T2>(T2 x) where T2 : I1, allows ref struct
{
Test(x);
}
static void Test<T>(T x) where T : I1
{
x.M1();
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (19,9): error CS9244: The type 'S1' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'C.Test<T>(T)'
// Test(new S1());
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "Test").WithArguments("C.Test<T>(T)", "T", "S1").WithLocation(19, 9),
// (24,9): error CS9244: The type 'T2' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'C.Test<T>(T)'
// Test(x);
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "Test").WithArguments("C.Test<T>(T)", "T", "T2").WithLocation(24, 9)
);
}
[Fact]
public void ImplementAnInterface_04()
{
var src = @"
interface I1
{
void M1();
}
ref struct S1 : I1
{
public void M1()
{
System.Console.Write(""S1.M1"");
}
}
class C
{
static void Main()
{
Test1(new S1());
System.Console.Write("" "");
Test2(new S1());
}
static void Test1<T>(T x) where T : I1, allows ref struct
{
x.M1();
}
static void Test2<T>(T x) where T : I1, allows ref struct
{
Test1(x);
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"S1.M1 S1.M1" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test1<T>(T)",
@"
{
// Code size 14 (0xe)
.maxstack 1
IL_0000: ldarga.s V_0
IL_0002: constrained. ""T""
IL_0008: callvirt ""void I1.M1()""
IL_000d: ret
}
");
}
[Fact]
public void ImplementAnInterface_05_Variance()
{
var src = @"
interface I1<in T>
{
void M1(T x);
}
interface I2<out T>
{
T M2();
}
ref struct S1 : I1<object>, I2<string>
{
public void M1(object x)
{
System.Console.Write(""S1.M1"");
System.Console.Write("" "");
System.Console.Write(x);
}
public string M2()
{
System.Console.Write(""S1.M2 "");
return ""z"";
}
}
class C
{
static void Main()
{
Test1(new S1(), ""y"");
System.Console.Write("" - "");
System.Console.Write(Test2(new S1()));
System.Console.Write("" - "");
Test3(new S1(), ""y"");
System.Console.Write("" - "");
System.Console.Write(Test4(new S1()));
}
static void Test1<T>(T x, string y) where T : I1<string>, allows ref struct
{
x.M1(y);
}
static object Test2<T>(T x) where T : I2<object>, allows ref struct
{
return x.M2();
}
static void Test3<T>(T x, string y) where T : I1<object>, allows ref struct
{
Test1(x, y);
}
static object Test4<T>(T x) where T : I2<string>, allows ref struct
{
return Test2(x);
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"S1.M1 y - S1.M2 z - S1.M1 y - S1.M2 z" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test1<T>(T, string)",
@"
{
// Code size 15 (0xf)
.maxstack 2
IL_0000: ldarga.s V_0
IL_0002: ldarg.1
IL_0003: constrained. ""T""
IL_0009: callvirt ""void I1<string>.M1(string)""
IL_000e: ret
}
");
verifier.VerifyIL("C.Test2<T>(T)",
@"
{
// Code size 14 (0xe)
.maxstack 1
IL_0000: ldarga.s V_0
IL_0002: constrained. ""T""
IL_0008: callvirt ""object I2<object>.M2()""
IL_000d: ret
}
");
}
[Fact]
public void ImplementAnInterface_06_DefaultImplementation()
{
var src1 = @"
public interface I1
{
virtual void M1() {}
static virtual void M2() {}
sealed void M3() {}
public class C1 {}
}
ref struct S1 : I1
{
}
ref struct S2 : I1
{
public void M1()
{
}
}
";
var src2 = @"
class C
{
static void Test1(I1 x)
{
x.M1();
x.M3();
}
static void Test2<T>(T x) where T : I1, allows ref struct
{
x.M1();
T.M2();
#line 100
x.M3();
_ = new T.C1();
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.NetCoreApp);
comp1.VerifyDiagnostics(
// (11,17): error CS9245: 'I1.M1()' cannot implement interface member 'I1.M1()' for ref struct 'S1'.
// ref struct S1 : I1
Diagnostic(ErrorCode.ERR_RefStructDoesNotSupportDefaultInterfaceImplementationForMember, "I1").WithArguments("I1.M1()", "I1.M1()", "S1").WithLocation(11, 17)
);
comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
// Specification suggests to report a warning for access to virtual (non-abstract) members.
// There is an open design question - https://github.com/dotnet/csharplang/blob/main/proposals/ref-struct-interfaces.md#warn-on-dim-invocation
comp2.VerifyDiagnostics(
// (100,9): error CS9246: A non-virtual instance interface member cannot be accessed on a type parameter that allows ref struct.
// x.M3();
Diagnostic(ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike, "x.M3").WithLocation(100, 9),
// (101,17): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter
// _ = new T.C1();
Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T.C1").WithArguments("T").WithLocation(101, 17)
);
}
[Fact]
public void ImplementAnInterface_07_DefaultImplementation()
{
var src1 = @"
public interface I1
{
virtual int P1 => 1;
static virtual int P2 => 2;
sealed int P3 => 3;
}
ref struct S1 : I1
{
}
ref struct S2 : I1
{
public int P1 => 21;
}
";
var src2 = @"
class C
{
static void Test1(I1 x)
{
_ = x.P1;
_ = x.P3;
}
static void Test2<T>(T x) where T : I1, allows ref struct
{
_ = x.P1;
_ = T.P2;
#line 100
_ = x.P3;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.NetCoreApp);
comp1.VerifyDiagnostics(
// (9,17): error CS9245: 'I1.P1.get' cannot implement interface member 'I1.P1.get' for ref struct 'S1'.
// ref struct S1 : I1
Diagnostic(ErrorCode.ERR_RefStructDoesNotSupportDefaultInterfaceImplementationForMember, "I1").WithArguments("I1.P1.get", "I1.P1.get", "S1").WithLocation(9, 17)
);
comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
// Specification suggests to report a warning for access to virtual (non-abstract) members.
// There is an open design question - https://github.com/dotnet/csharplang/blob/main/proposals/ref-struct-interfaces.md#warn-on-dim-invocation
comp2.VerifyDiagnostics(
// (100,13): error CS9246: A non-virtual instance interface member cannot be accessed on a type parameter that allows ref struct.
// _ = x.P3;
Diagnostic(ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike, "x.P3").WithLocation(100, 13)
);
}
[Fact]
public void ImplementAnInterface_08_DefaultImplementation()
{
var src1 = @"
public interface I1
{
virtual int P1 {set{}}
static virtual int P2 {set{}}
sealed int P3 {set{}}
}
ref struct S1 : I1
{
}
ref struct S2 : I1
{
public int P1 {set{}}
}
";
var src2 = @"
class C
{
static void Test1(I1 x)
{
x.P1 = 11;
x.P3 = 11;
}
static void Test2<T>(T x) where T : I1, allows ref struct
{
x.P1 = 123;
T.P2 = 123;
x.P3 = 123;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.NetCoreApp);
comp1.VerifyDiagnostics(
// (9,17): error CS9245: 'I1.P1.set' cannot implement interface member 'I1.P1.set' for ref struct 'S1'.
// ref struct S1 : I1
Diagnostic(ErrorCode.ERR_RefStructDoesNotSupportDefaultInterfaceImplementationForMember, "I1").WithArguments("I1.P1.set", "I1.P1.set", "S1").WithLocation(9, 17)
);
comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
// Specification suggests to report a warning for access to virtual (non-abstract) members.
// There is an open design question - https://github.com/dotnet/csharplang/blob/main/proposals/ref-struct-interfaces.md#warn-on-dim-invocation
comp2.VerifyDiagnostics(
// (14,9): error CS9246: A non-virtual instance interface member cannot be accessed on a type parameter that allows ref struct.
// x.P3 = 123;
Diagnostic(ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike, "x.P3").WithLocation(14, 9)
);
}
[Fact]
public void ImplementAnInterface_09_DefaultImplementation()
{
var src1 = @"
public interface I1
{
virtual int this[int x] => 1;
}
public interface I2
{
sealed int this[long x] => 1;
}
ref struct S1 : I1, I2
{
}
ref struct S2 : I1, I2
{
public int this[int x] => 21;
}
";
var src2 = @"
class C
{
static void Test1(I1 x)
{
_ = x[1];
}
static void Test1(I2 x)
{
_ = x[2];
}
static void Test2<T>(T x) where T : I1, allows ref struct
{
_ = x[3];
}
static void Test3<T>(T x) where T : I2, allows ref struct
{
_ = x[4];
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.NetCoreApp);
comp1.VerifyDiagnostics(
// (12,17): error CS9245: 'I1.this[int].get' cannot implement interface member 'I1.this[int].get' for ref struct 'S1'.
// ref struct S1 : I1, I2
Diagnostic(ErrorCode.ERR_RefStructDoesNotSupportDefaultInterfaceImplementationForMember, "I1").WithArguments("I1.this[int].get", "I1.this[int].get", "S1").WithLocation(12, 17)
);
comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
// Specification suggests to report a warning for access to virtual (non-abstract) members.
// There is an open design question - https://github.com/dotnet/csharplang/blob/main/proposals/ref-struct-interfaces.md#warn-on-dim-invocation
comp2.VerifyDiagnostics(
// (21,13): error CS9246: A non-virtual instance interface member cannot be accessed on a type parameter that allows ref struct.
// _ = x[4];
Diagnostic(ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike, "x[4]").WithLocation(21, 13)
);
}
[Fact]
public void ImplementAnInterface_10_DefaultImplementation()
{
var src1 = @"
public interface I1
{
virtual int this[int x] {set{}}
}
public interface I2
{
sealed int this[long x] {set{}}
}
ref struct S1 : I1, I2
{
}
ref struct S2 : I1, I2
{
public int this[int x] {set{}}
}
";
var src2 = @"
class C
{
static void Test1(I1 x)
{
x[1] = 1;
}
static void Test1(I2 x)
{
x[2] = 2;
}
static void Test2<T>(T x) where T : I1, allows ref struct
{
x[3] = 3;
}
static void Test3<T>(T x) where T : I2, allows ref struct
{
x[4] = 4;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.NetCoreApp);
comp1.VerifyDiagnostics(
// (12,17): error CS9245: 'I1.this[int].set' cannot implement interface member 'I1.this[int].set' for ref struct 'S1'.
// ref struct S1 : I1, I2
Diagnostic(ErrorCode.ERR_RefStructDoesNotSupportDefaultInterfaceImplementationForMember, "I1").WithArguments("I1.this[int].set", "I1.this[int].set", "S1").WithLocation(12, 17)
);
comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
// Specification suggests to report a warning for access to virtual (non-abstract) members.
// There is an open design question - https://github.com/dotnet/csharplang/blob/main/proposals/ref-struct-interfaces.md#warn-on-dim-invocation
comp2.VerifyDiagnostics(
// (21,9): error CS9246: A non-virtual instance interface member cannot be accessed on a type parameter that allows ref struct.
// x[4] = 4;
Diagnostic(ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike, "x[4]").WithLocation(21, 9)
);
}
[Fact]
public void ImplementAnInterface_11_DefaultImplementation()
{
var src1 = @"
public interface I1
{
virtual event System.Action E1 {add{} remove{}}
static virtual event System.Action E2 {add{} remove{}}
sealed event System.Action E3 {add{} remove{}}
}
ref struct S1 : I1
{
}
ref struct S2 : I1
{
#pragma warning disable CS0067 // The event 'S2.E1' is never used
public event System.Action E1;
}
";
var src2 = @"
class C
{
static void Test1(I1 x)
{
x.E1 += null;
x.E3 += null;
x.E1 -= null;
x.E3 -= null;
}
static void Test2<T>(T x) where T : I1, allows ref struct
{
x.E1 += null;
T.E2 += null;
#line 100
x.E3 += null;
x.E1 -= null;
T.E2 -= null;
#line 200
x.E3 -= null;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.NetCoreApp);
comp1.VerifyDiagnostics(
// (9,17): error CS9245: 'I1.E1.add' cannot implement interface member 'I1.E1.add' for ref struct 'S1'.
// ref struct S1 : I1
Diagnostic(ErrorCode.ERR_RefStructDoesNotSupportDefaultInterfaceImplementationForMember, "I1").WithArguments("I1.E1.add", "I1.E1.add", "S1").WithLocation(9, 17),
// (9,17): error CS9245: 'I1.E1.remove' cannot implement interface member 'I1.E1.remove' for ref struct 'S1'.
// ref struct S1 : I1
Diagnostic(ErrorCode.ERR_RefStructDoesNotSupportDefaultInterfaceImplementationForMember, "I1").WithArguments("I1.E1.remove", "I1.E1.remove", "S1").WithLocation(9, 17)
);
comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
// Specification suggests to report a warning for access to virtual (non-abstract) members.
// There is an open design question - https://github.com/dotnet/csharplang/blob/main/proposals/ref-struct-interfaces.md#warn-on-dim-invocation
comp2.VerifyDiagnostics(
// (100,9): error CS9246: A non-virtual instance interface member cannot be accessed on a type parameter that allows ref struct.
// x.E3 += null;
Diagnostic(ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike, "x.E3 += null").WithLocation(100, 9),
// (200,9): error CS9246: A non-virtual instance interface member cannot be accessed on a type parameter that allows ref struct.
// x.E3 -= null;
Diagnostic(ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike, "x.E3 -= null").WithLocation(200, 9)
);
}
[Fact]
public void ImplementAnInterface_12_Variance_ErrorScenarios()
{
var src = @"
interface I<T>
{
void M(T t);
}
interface IOut<out T>
{
T MOut();
}
interface IIn<in T>
{
void MIn(T x);
}
ref struct S : I<object>, IOut<object>, IIn<string>
{
public void M(object o) { }
public object MOut() => null;
public void MIn(string x) {}
}
class Program
{
static void Main()
{
#line 19
Test1(new S());
Test2(new S());
Test3(new S());
Test4(new S());
Test5(new S());
Test6(new S());
}
static void Test1<T>(T x) where T : I<string>, allows ref struct
{
}
static void Test2<T>(T x) where T : IOut<string>, allows ref struct
{
}
static void Test3<T>(T x) where T : IIn<object>, allows ref struct
{
}
static void Test4<T4>(T4 x) where T4 : I<object>, allows ref struct
{
Test1(x);
}
static void Test5<T5>(T5 x) where T5 : IOut<object>, allows ref struct
{
Test2(x);
}
static void Test6<T6>(T6 x) where T6 : IIn<string>, allows ref struct
{
Test3(x);
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (19,9): error CS0315: The type 'S' cannot be used as type parameter 'T' in the generic type or method 'Program.Test1<T>(T)'. There is no boxing conversion from 'S' to 'I<string>'.
// Test1(new S());
Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "Test1").WithArguments("Program.Test1<T>(T)", "I<string>", "T", "S").WithLocation(19, 9),
// (20,9): error CS0315: The type 'S' cannot be used as type parameter 'T' in the generic type or method 'Program.Test2<T>(T)'. There is no boxing conversion from 'S' to 'IOut<string>'.
// Test2(new S());
Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "Test2").WithArguments("Program.Test2<T>(T)", "IOut<string>", "T", "S").WithLocation(20, 9),
// (21,9): error CS0315: The type 'S' cannot be used as type parameter 'T' in the generic type or method 'Program.Test3<T>(T)'. There is no boxing conversion from 'S' to 'IIn<object>'.
// Test3(new S());
Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "Test3").WithArguments("Program.Test3<T>(T)", "IIn<object>", "T", "S").WithLocation(21, 9),
// (39,9): error CS0314: The type 'T4' cannot be used as type parameter 'T' in the generic type or method 'Program.Test1<T>(T)'. There is no boxing conversion or type parameter conversion from 'T4' to 'I<string>'.
// Test1(x);
Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar, "Test1").WithArguments("Program.Test1<T>(T)", "I<string>", "T", "T4").WithLocation(39, 9),
// (43,9): error CS0314: The type 'T5' cannot be used as type parameter 'T' in the generic type or method 'Program.Test2<T>(T)'. There is no boxing conversion or type parameter conversion from 'T5' to 'IOut<string>'.
// Test2(x);
Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar, "Test2").WithArguments("Program.Test2<T>(T)", "IOut<string>", "T", "T5").WithLocation(43, 9),
// (47,9): error CS0314: The type 'T6' cannot be used as type parameter 'T' in the generic type or method 'Program.Test3<T>(T)'. There is no boxing conversion or type parameter conversion from 'T6' to 'IIn<object>'.
// Test3(x);
Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar, "Test3").WithArguments("Program.Test3<T>(T)", "IIn<object>", "T", "T6").WithLocation(47, 9)
);
}
[Fact]
public void Using_01()
{
var src = @"
ref struct S2 : System.IDisposable
{
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
using (new S2())
{
System.Console.Write(123);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 26 (0x1a)
.maxstack 1
.locals init (S2 V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj ""S2""
.try
{
IL_0008: ldc.i4.s 123
IL_000a: call ""void System.Console.Write(int)""
IL_000f: leave.s IL_0019
}
finally
{
IL_0011: ldloca.s V_0
IL_0013: call ""void S2.Dispose()""
IL_0018: endfinally
}
IL_0019: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S2()')
Value:
IObjectCreationOperation (Constructor: S2..ctor()) (OperationKind.ObjectCreation, Type: S2) (Syntax: 'new S2()')
Arguments(0)
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B4]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (1)
IInvocationOperation ( void S2.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S2()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S2()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
}
[Fact]
public void Using_02()
{
var src = @"
ref struct S2 : System.IDisposable
{
void System.IDisposable.Dispose()
{
System.Console.Write('D');
}
}
struct S3 : System.IDisposable
{
void System.IDisposable.Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test1();
Test2();
}
static void Test1()
{
using (new S2())
{
System.Console.Write(123);
}
}
static void Test2()
{
using (new S3())
{
System.Console.Write(456);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D456D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test1",
@"
{
// Code size 32 (0x20)
.maxstack 1
.locals init (S2 V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj ""S2""
.try
{
IL_0008: ldc.i4.s 123
IL_000a: call ""void System.Console.Write(int)""
IL_000f: leave.s IL_001f
}
finally
{
IL_0011: ldloca.s V_0
IL_0013: constrained. ""S2""
IL_0019: callvirt ""void System.IDisposable.Dispose()""
IL_001e: endfinally
}
IL_001f: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test1").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S2()')
Value:
IObjectCreationOperation (Constructor: S2..ctor()) (OperationKind.ObjectCreation, Type: S2) (Syntax: 'new S2()')
Arguments(0)
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B4]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S2()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S2()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
}
[Fact]
public void Using_03()
{
var src = @"
ref struct S2 : System.IDisposable
{
void System.IDisposable.Dispose() => throw null;
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
using (var x = new S2())
{
System.Console.Write(123);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 26 (0x1a)
.maxstack 1
.locals init (S2 V_0) //x
IL_0000: ldloca.s V_0
IL_0002: initobj ""S2""
.try
{
IL_0008: ldc.i4.s 123
IL_000a: call ""void System.Console.Write(int)""
IL_000f: leave.s IL_0019
}
finally
{
IL_0011: ldloca.s V_0
IL_0013: call ""void S2.Dispose()""
IL_0018: endfinally
}
IL_0019: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
Locals: [S2 x]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: S2, IsImplicit) (Syntax: 'x = new S2()')
Left:
ILocalReferenceOperation: x (IsDeclaration: True) (OperationKind.LocalReference, Type: S2, IsImplicit) (Syntax: 'x = new S2()')
Right:
IObjectCreationOperation (Constructor: S2..ctor()) (OperationKind.ObjectCreation, Type: S2) (Syntax: 'new S2()')
Arguments(0)
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B4]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (1)
IInvocationOperation ( void S2.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'x = new S2()')
Instance Receiver:
ILocalReferenceOperation: x (OperationKind.LocalReference, Type: S2, IsImplicit) (Syntax: 'x = new S2()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
}
[Fact]
public void Using_04()
{
var src = @"
ref struct S2 : System.IDisposable
{
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test(new S2());
}
static void Test<T>(T t) where T : System.IDisposable, allows ref struct
{
using (t)
{
System.Console.Write(123);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
// The code boxes type parameter that allows ref struct, however 'box' followed by 'brfalse' is
// documented as a valid sequence at https://github.com/dotnet/runtime/blob/main/docs/design/features/byreflike-generics.md#special-il-sequences
//
// IL_000b: ldloc.0
// IL_000c: box ""T""
// IL_0011: brfalse.s IL_0020
verifier.VerifyIL("C.Test<T>(T)",
@"
{
// Code size 34 (0x22)
.maxstack 1
.locals init (T V_0)
IL_0000: ldarg.0
IL_0001: stloc.0
.try
{
IL_0002: ldc.i4.s 123
IL_0004: call ""void System.Console.Write(int)""
IL_0009: leave.s IL_0021
}
finally
{
IL_000b: ldloc.0
IL_000c: box ""T""
IL_0011: brfalse.s IL_0020
IL_0013: ldloca.s V_0
IL_0015: constrained. ""T""
IL_001b: callvirt ""void System.IDisposable.Dispose()""
IL_0020: endfinally
}
IL_0021: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't')
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B6]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B5]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: 't')
Next (Regular) Block[B4]
Block[B4] - Block
Predecessors: [B3]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B3] [B4]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B6] - Exit
Predecessors: [B2]
Statements (0)
""");
}
[Fact]
public void Using_05()
{
var src = @"
ref struct S2 : System.IDisposable
{
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test(new S2());
}
static void Test<T>(T t) where T : struct, System.IDisposable, allows ref struct
{
using (t)
{
System.Console.Write(123);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<T>(T)",
@"
{
// Code size 26 (0x1a)
.maxstack 1
.locals init (T V_0)
IL_0000: ldarg.0
IL_0001: stloc.0
.try
{
IL_0002: ldc.i4.s 123
IL_0004: call ""void System.Console.Write(int)""
IL_0009: leave.s IL_0019
}
finally
{
IL_000b: ldloca.s V_0
IL_000d: constrained. ""T""
IL_0013: callvirt ""void System.IDisposable.Dispose()""
IL_0018: endfinally
}
IL_0019: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't')
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B4]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: 't')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/73559")]
public void Using_06()
{
var src = @"
interface IMyDisposable
{
void Dispose();
}
ref struct S2 : IMyDisposable
{
public void Dispose()
{
}
}
class C
{
static void Main()
{
Test(new S2(), null);
}
static void Test<T>(T t, IMyDisposable s) where T : IMyDisposable, allows ref struct
{
using (t)
{
}
using (s)
{
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (23,16): error CS1674: 'T': type used in a using statement must implement 'System.IDisposable'.
// using (t)
Diagnostic(ErrorCode.ERR_NoConvToIDisp, "t").WithArguments("T").WithLocation(23, 16),
// (27,16): error CS1674: 'IMyDisposable': type used in a using statement must implement 'System.IDisposable'.
// using (s)
Diagnostic(ErrorCode.ERR_NoConvToIDisp, "s").WithArguments("IMyDisposable").WithLocation(27, 16)
);
}
[Fact]
public void Using_LanguageVersion_01()
{
var src1 = @"
public ref struct S2 : System.IDisposable
{
public void Dispose()
{
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Main()
{
using (new S2())
{
}
using (var s = new S2())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Fact]
public void Using_LanguageVersion_02()
{
var src1 = @"
public ref struct S2 : System.IDisposable
{
void System.IDisposable.Dispose()
{
}
}
public struct S3 : System.IDisposable
{
void System.IDisposable.Dispose()
{
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Main()
{
using (new S2())
{
}
using (var s = new S2())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (6,16): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// using (new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new S2()").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 16),
// (10,16): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// using (var s = new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var s = new S2()").WithArguments("ref struct interfaces", "13.0").WithLocation(10, 16)
);
comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (2,24): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// public ref struct S2 : System.IDisposable
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "System.IDisposable").WithArguments("ref struct interfaces", "13.0").WithLocation(2, 24)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
var src3 = @"
class C
{
static void Main()
{
using (new S3())
{
}
using (var s = new S3())
{
}
}
}
";
var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp3.VerifyEmitDiagnostics();
}
[Fact]
public void Using_LanguageVersion_04()
{
var src = @"
class C
{
static void Test<T>(T t) where T : System.IDisposable, allows ref struct
{
using (t)
{
}
using (var s = t)
{
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp.VerifyEmitDiagnostics(
// (4,67): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static void Test<T>(T t) where T : System.IDisposable, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(4, 67)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics();
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IEnumerableT_01()
{
var src = @"
using System.Collections.Generic;
ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return Get123();
}
static IEnumerator<int> Get123()
{
yield return 123;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}
class C
{
static void Main()
{
foreach (var i in new S())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 49 (0x31)
.maxstack 2
.locals init (System.Collections.Generic.IEnumerator<int> V_0,
S V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S""
IL_0009: call ""System.Collections.Generic.IEnumerator<int> S.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001c
IL_0011: ldloc.0
IL_0012: callvirt ""int System.Collections.Generic.IEnumerator<int>.Current.get""
IL_0017: call ""void System.Console.Write(int)""
IL_001c: ldloc.0
IL_001d: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0022: brtrue.s IL_0011
IL_0024: leave.s IL_0030
}
finally
{
IL_0026: ldloc.0
IL_0027: brfalse.s IL_002f
IL_0029: ldloc.0
IL_002a: callvirt ""void System.IDisposable.Dispose()""
IL_002f: endfinally
}
IL_0030: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation ( System.Collections.Generic.IEnumerator<System.Int32> S.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ImplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> S.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> S.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
public void Foreach_IEnumerableT_02()
{
var src = @"
using System.Collections.Generic;
ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return Get123();
}
static IEnumerator<int> Get123()
{
yield return 123;
}
IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}
class C
{
static void Main()
{
foreach (var i in new S())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 49 (0x31)
.maxstack 2
.locals init (System.Collections.Generic.IEnumerator<int> V_0,
S V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S""
IL_0009: call ""System.Collections.Generic.IEnumerator<int> S.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001c
IL_0011: ldloc.0
IL_0012: callvirt ""int System.Collections.Generic.IEnumerator<int>.Current.get""
IL_0017: call ""void System.Console.Write(int)""
IL_001c: ldloc.0
IL_001d: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0022: brtrue.s IL_0011
IL_0024: leave.s IL_0030
}
finally
{
IL_0026: ldloc.0
IL_0027: brfalse.s IL_002f
IL_0029: ldloc.0
IL_002a: callvirt ""void System.IDisposable.Dispose()""
IL_002f: endfinally
}
IL_0030: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation ( System.Collections.Generic.IEnumerator<System.Int32> S.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ImplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> S.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> S.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
public void Foreach_IEnumerableT_03()
{
var src = @"
using System.Collections.Generic;
ref struct S : IEnumerable<int>
{
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
return Get123();
}
static IEnumerator<int> Get123()
{
yield return 123;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => Get123();
}
struct S2 : IEnumerable<int>
{
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
return Get456();
}
static IEnumerator<int> Get456()
{
yield return 456;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => Get456();
}
class C
{
static void Main()
{
Test1();
Test2();
}
static void Test1()
{
foreach (var i in new S())
{
System.Console.Write(i);
}
}
static void Test2()
{
foreach (var i in new S2())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123456" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test1",
@"
{
// Code size 55 (0x37)
.maxstack 2
.locals init (System.Collections.Generic.IEnumerator<int> V_0,
S V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S""
IL_0009: constrained. ""S""
IL_000f: callvirt ""System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()""
IL_0014: stloc.0
.try
{
IL_0015: br.s IL_0022
IL_0017: ldloc.0
IL_0018: callvirt ""int System.Collections.Generic.IEnumerator<int>.Current.get""
IL_001d: call ""void System.Console.Write(int)""
IL_0022: ldloc.0
IL_0023: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0028: brtrue.s IL_0017
IL_002a: leave.s IL_0036
}
finally
{
IL_002c: ldloc.0
IL_002d: brfalse.s IL_0035
IL_002f: ldloc.0
IL_0030: callvirt ""void System.IDisposable.Dispose()""
IL_0035: endfinally
}
IL_0036: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test1").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation (virtual System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ImplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().First();
Assert.Equal("new S()", foreachSyntax.Expression.ToString());
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerableT_04(bool addStructConstraint)
{
var src = @"
using System.Collections.Generic;
ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return Get123();
}
static IEnumerator<int> Get123()
{
yield return 123;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}
class C
{
static void Main()
{
Test(new S());
}
static void Test<T>(T t) where T : " + (addStructConstraint ? "struct, " : "") + @"IEnumerable<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<T>(T)",
@"
{
// Code size 48 (0x30)
.maxstack 1
.locals init (System.Collections.Generic.IEnumerator<int> V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""T""
IL_0008: callvirt ""System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_001b
IL_0010: ldloc.0
IL_0011: callvirt ""int System.Collections.Generic.IEnumerator<int>.Current.get""
IL_0016: call ""void System.Console.Write(int)""
IL_001b: ldloc.0
IL_001c: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0021: brtrue.s IL_0010
IL_0023: leave.s IL_002f
}
finally
{
IL_0025: ldloc.0
IL_0026: brfalse.s IL_002e
IL_0028: ldloc.0
IL_0029: callvirt ""void System.IDisposable.Dispose()""
IL_002e: endfinally
}
IL_002f: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ImplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerableT_05(bool addStructConstraint)
{
var src = @"
using System.Collections.Generic;
interface IMyEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
ref struct S : IMyEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return Get123();
}
static IEnumerator<int> Get123()
{
yield return 123;
}
}
class C
{
static void Main()
{
Test(new S());
}
static void Test<T>(T t) where T : " + (addStructConstraint ? "struct, " : "") + @"IMyEnumerable<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<T>(T)",
@"
{
// Code size 48 (0x30)
.maxstack 1
.locals init (System.Collections.Generic.IEnumerator<int> V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""T""
IL_0008: callvirt ""System.Collections.Generic.IEnumerator<int> IMyEnumerable<int>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_001b
IL_0010: ldloc.0
IL_0011: callvirt ""int System.Collections.Generic.IEnumerator<int>.Current.get""
IL_0016: call ""void System.Console.Write(int)""
IL_001b: ldloc.0
IL_001c: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0021: brtrue.s IL_0010
IL_0023: leave.s IL_002f
}
finally
{
IL_0025: ldloc.0
IL_0026: brfalse.s IL_002e
IL_0028: ldloc.0
IL_0029: callvirt ""void System.IDisposable.Dispose()""
IL_002e: endfinally
}
IL_002f: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual System.Collections.Generic.IEnumerator<System.Int32> IMyEnumerable<System.Int32>.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ImplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> IMyEnumerable<System.Int32>.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> IMyEnumerable<System.Int32>.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerableT_06(bool addStructConstraint)
{
var src = @"
using System.Collections.Generic;
interface IMyEnumerable1<T>
{
IEnumerator<int> GetEnumerator();
}
interface IMyEnumerable2<T>
{
IEnumerator<int> GetEnumerator();
}
ref struct S : IMyEnumerable1<int>, IMyEnumerable2<int>
{
public IEnumerator<int> GetEnumerator()
{
return Get123();
}
static IEnumerator<int> Get123()
{
yield return 123;
}
}
class C
{
static void Main()
{
Test(new S());
}
static void Test<T>(T t) where T : " + (addStructConstraint ? "struct, " : "") + @"IMyEnumerable1<int>, IMyEnumerable2<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (37,27): warning CS0278: 'T' does not implement the 'collection' pattern. 'IMyEnumerable1<int>.GetEnumerator()' is ambiguous with 'IMyEnumerable2<int>.GetEnumerator()'.
// foreach (var i in t)
Diagnostic(ErrorCode.WRN_PatternIsAmbiguous, "t").WithArguments("T", "collection", "IMyEnumerable1<int>.GetEnumerator()", "IMyEnumerable2<int>.GetEnumerator()").WithLocation(37, 27),
// (37,27): error CS1579: foreach statement cannot operate on variables of type 'T' because 'T' does not contain a public instance or extension definition for 'GetEnumerator'
// foreach (var i in t)
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "t").WithArguments("T", "GetEnumerator").WithLocation(37, 27)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
Assert.Null(info.GetEnumeratorMethod);
Assert.Null(info.ElementType);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.Null(op.Info);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerableT_07(bool addStructConstraint)
{
var src = @"
using System.Collections.Generic;
interface IMyEnumerable1<T>
{
IEnumerator<int> GetEnumerator();
}
interface IMyEnumerable2<T>
{
IEnumerator<int> GetEnumerator();
}
ref struct S : IMyEnumerable1<int>, IMyEnumerable2<int>, IEnumerable<int>
{
IEnumerator<int> IMyEnumerable1<int>.GetEnumerator() => throw null;
IEnumerator<int> IMyEnumerable2<int>.GetEnumerator() => throw null;
public IEnumerator<int> GetEnumerator()
{
return Get123();
}
static IEnumerator<int> Get123()
{
yield return 123;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}
class C
{
static void Main()
{
Test(new S());
}
static void Test<T>(T t) where T : " + (addStructConstraint ? "struct, " : "") + @"IMyEnumerable1<int>, IMyEnumerable2<int>, IEnumerable<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(
// (41,27): warning CS0278: 'T' does not implement the 'collection' pattern. 'IMyEnumerable1<int>.GetEnumerator()' is ambiguous with 'IMyEnumerable2<int>.GetEnumerator()'.
// foreach (var i in t)
Diagnostic(ErrorCode.WRN_PatternIsAmbiguous, "t").WithArguments("T", "collection", "IMyEnumerable1<int>.GetEnumerator()", "IMyEnumerable2<int>.GetEnumerator()").WithLocation(41, 27)
);
verifier.VerifyIL("C.Test<T>(T)",
@"
{
// Code size 48 (0x30)
.maxstack 1
.locals init (System.Collections.Generic.IEnumerator<int> V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""T""
IL_0008: callvirt ""System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_001b
IL_0010: ldloc.0
IL_0011: callvirt ""int System.Collections.Generic.IEnumerator<int>.Current.get""
IL_0016: call ""void System.Console.Write(int)""
IL_001b: ldloc.0
IL_001c: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0021: brtrue.s IL_0010
IL_0023: leave.s IL_002f
}
finally
{
IL_0025: ldloc.0
IL_0026: brfalse.s IL_002e
IL_0028: ldloc.0
IL_0029: callvirt ""void System.IDisposable.Dispose()""
IL_002e: endfinally
}
IL_002f: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ImplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerator<System.Int32>, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IEnumerator<System.Int32> System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
public void Foreach_IEnumerableT_LanguageVersion_01()
{
var src1 = @"
using System.Collections.Generic;
public ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Main()
{
foreach (var i in new S())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IEnumerableT_LanguageVersion_03()
{
var src1 = @"
using System.Collections.Generic;
public ref struct S : IEnumerable<int>
{
IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
}
public struct S2 : IEnumerable<int>
{
IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Main()
{
foreach (var i in new S())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
// Even though semantic analysis didn't produce any errors in C# 12 compiler, an attempt to emit was failing with
// "Unable to determine specific cause of the failure" error.
comp2.VerifyEmitDiagnostics(
// (6,27): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// foreach (var i in new S())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new S()").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 27)
);
comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (4,23): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// public ref struct S : IEnumerable<int>
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "IEnumerable<int>").WithArguments("ref struct interfaces", "13.0").WithLocation(4, 23)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
var src3 = @"
class C
{
static void Main()
{
foreach (var i in new S2())
{
}
}
}
";
var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp3.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IEnumerableT_LanguageVersion_04()
{
var src = @"
using System.Collections.Generic;
class C
{
static void Test<T>(T t) where T : IEnumerable<int>, allows ref struct
{
foreach (var i in t)
{
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp.VerifyEmitDiagnostics(
// (6,65): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static void Test<T>(T t) where T : IEnumerable<int>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(6, 65)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics();
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IEnumerableT_LanguageVersion_05()
{
var src1 = @"
using System.Collections.Generic;
public interface IMyEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Test<T>(T t) where T : IMyEnumerable<int>, allows ref struct
{
foreach (var i in t)
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (4,67): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static void Test<T>(T t) where T : IMyEnumerable<int>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(4, 67)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_01(bool s1IsRefStruct)
{
var src = @"
using System.Collections.Generic;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator<int>
{
bool stop;
public int Current => 123;
object System.Collections.IEnumerator.Current => Current;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 49 (0x31)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""int S2.Current.get""
IL_0018: call ""void System.Console.Write(int)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: leave.s IL_0030
}
finally
{
IL_0028: ldloca.s V_0
IL_002a: call ""void S2.Dispose()""
IL_002f: endfinally
}
IL_0030: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B5]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 S2.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (1)
IInvocationOperation ( void S2.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B5] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void S2.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
AssertEx.Equal("void S2.Dispose()", op.Info.PatternDisposeMethod.ToTestDisplayString());
Assert.True(op.Info.DisposeArguments.IsEmpty);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_02(bool s1IsRefStruct)
{
var src = @"
using System.Collections.Generic;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator<int>
{
bool stop;
public int Current => 123;
object System.Collections.IEnumerator.Current => Current;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
int IEnumerator<int>.Current => throw null;
bool System.Collections.IEnumerator.MoveNext() => throw null;
void System.IDisposable.Dispose() => throw null;
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 49 (0x31)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""int S2.Current.get""
IL_0018: call ""void System.Console.Write(int)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: leave.s IL_0030
}
finally
{
IL_0028: ldloca.s V_0
IL_002a: call ""void S2.Dispose()""
IL_002f: endfinally
}
IL_0030: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B5]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 S2.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (1)
IInvocationOperation ( void S2.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B5] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void S2.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
AssertEx.Equal("void S2.Dispose()", op.Info.PatternDisposeMethod.ToTestDisplayString());
Assert.True(op.Info.DisposeArguments.IsEmpty);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_03(bool s1IsRefStruct, bool currentIsPublic, bool moveNextIsPublic, bool disposeIsPublic)
{
if (currentIsPublic && moveNextIsPublic)
{
return;
}
var src = @"
using System.Collections.Generic;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator<int>
{
bool stop;
" + (currentIsPublic ? "public int " : "int IEnumerator<int>.") + @"Current => 123;
object System.Collections.IEnumerator.Current => 123;
" + (moveNextIsPublic ? "public bool " : "bool System.Collections.IEnumerator.") + @"MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
" + (disposeIsPublic ? "public void " : "void System.IDisposable.") + @"Dispose()
{
System.Console.Write('D');
}
public void Reset() { }
}
class C
{
static void Main()
{
#line 100
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
if (!currentIsPublic)
{
comp.VerifyDiagnostics(
// (100,27): error CS0117: 'S2' does not contain a definition for 'Current'
// foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_NoSuchMember, "new S1()").WithArguments("S2", "Current").WithLocation(100, 27),
// (100,27): error CS0202: foreach requires that the return type 'S2' of 'S1.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property
// foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new S1()").WithArguments("S2", "S1.GetEnumerator()").WithLocation(100, 27)
);
}
else
{
Assert.False(moveNextIsPublic);
comp.VerifyDiagnostics(
// (100,27): error CS0117: 'S2' does not contain a definition for 'MoveNext'
// foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_NoSuchMember, "new S1()").WithArguments("S2", "MoveNext").WithLocation(100, 27),
// (100,27): error CS0202: foreach requires that the return type 'S2' of 'S1.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property
// foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new S1()").WithArguments("S2", "S1.GetEnumerator()").WithLocation(100, 27)
);
}
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(1):
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1, IsInvalid) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Next (Regular) Block[B2]
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B4]
IInvalidOperation (OperationKind.Invalid, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(1):
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(0)
Next (Regular) Block[B3]
Entering: {R1}
.locals {R1}
{
Locals: [var i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsImplicit) (Syntax: 'var')
Right:
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(1):
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(0)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvalidOperation (OperationKind.Invalid, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Children(2):
IOperation: (OperationKind.None, Type: System.Console) (Syntax: 'System.Console')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: var) (Syntax: 'i')
Next (Regular) Block[B2]
Leaving: {R1}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
Assert.Null(info.ElementType);
Assert.Null(info.MoveNextMethod);
Assert.Null(info.CurrentProperty);
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.Null(op.Info);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_04(bool s1IsRefStruct, bool addExplicitImplementationOfCurrentAndMoveNext)
{
var src = @"
using System.Collections.Generic;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator<int>
{
bool stop;
public int Current => 123;
object System.Collections.IEnumerator.Current => Current;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
" +
(addExplicitImplementationOfCurrentAndMoveNext ?
@"
int IEnumerator<int>.Current => throw null;
bool System.Collections.IEnumerator.MoveNext() => throw null;
"
:
"") +
@"
void System.IDisposable.Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 55 (0x37)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""int S2.Current.get""
IL_0018: call ""void System.Console.Write(int)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: leave.s IL_0036
}
finally
{
IL_0028: ldloca.s V_0
IL_002a: constrained. ""S2""
IL_0030: callvirt ""void System.IDisposable.Dispose()""
IL_0035: endfinally
}
IL_0036: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B5]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 S2.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B5] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void System.IDisposable.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_05(bool s1IsRefStruct, bool addStructConstraintToTEnumerable)
{
var src = @"
using System.Collections.Generic;
interface IGetEnumerator<TEnumerator> where TEnumerator : IEnumerator<int>, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator<int>
{
bool stop;
public int Current => 123;
object System.Collections.IEnumerator.Current => Current;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : IEnumerator<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<TEnumerable, TEnumerator>(TEnumerable)",
@"
{
// Code size 74 (0x4a)
.maxstack 1
.locals init (TEnumerator V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""TEnumerable""
IL_0008: callvirt ""TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_0022
IL_0010: ldloca.s V_0
IL_0012: constrained. ""TEnumerator""
IL_0018: callvirt ""int System.Collections.Generic.IEnumerator<int>.Current.get""
IL_001d: call ""void System.Console.Write(int)""
IL_0022: ldloca.s V_0
IL_0024: constrained. ""TEnumerator""
IL_002a: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_002f: brtrue.s IL_0010
IL_0031: leave.s IL_0049
}
finally
{
IL_0033: ldloc.0
IL_0034: box ""TEnumerator""
IL_0039: brfalse.s IL_0048
IL_003b: ldloca.s V_0
IL_003d: constrained. ""TEnumerator""
IL_0043: callvirt ""void System.IDisposable.Dispose()""
IL_0048: endfinally
}
IL_0049: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()) (OperationKind.Invocation, Type: TEnumerator, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: TEnumerable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: TEnumerable) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void System.IDisposable.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_06(bool s1IsRefStruct, bool addStructConstraintToTEnumerable)
{
var src = @"
using System.Collections.Generic;
interface IGetEnumerator<TEnumerator> where TEnumerator : IEnumerator<int>, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator<int>
{
bool stop;
public int Current => 123;
object System.Collections.IEnumerator.Current => Current;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : struct, IEnumerator<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<TEnumerable, TEnumerator>(TEnumerable)",
@"
{
// Code size 66 (0x42)
.maxstack 1
.locals init (TEnumerator V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""TEnumerable""
IL_0008: callvirt ""TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_0022
IL_0010: ldloca.s V_0
IL_0012: constrained. ""TEnumerator""
IL_0018: callvirt ""int System.Collections.Generic.IEnumerator<int>.Current.get""
IL_001d: call ""void System.Console.Write(int)""
IL_0022: ldloca.s V_0
IL_0024: constrained. ""TEnumerator""
IL_002a: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_002f: brtrue.s IL_0010
IL_0031: leave.s IL_0041
}
finally
{
IL_0033: ldloca.s V_0
IL_0035: constrained. ""TEnumerator""
IL_003b: callvirt ""void System.IDisposable.Dispose()""
IL_0040: endfinally
}
IL_0041: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()) (OperationKind.Invocation, Type: TEnumerator, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: TEnumerable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: TEnumerable) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B5]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B5] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void System.IDisposable.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 System.Collections.Generic.IEnumerator<System.Int32>.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_07(bool s1IsRefStruct, bool addStructConstraintToTEnumerable)
{
var src = @"
interface IMyEnumerator<T> : System.IDisposable
{
T Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : IMyEnumerator<int>, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IMyEnumerator<int>
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : IMyEnumerator<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<TEnumerable, TEnumerator>(TEnumerable)",
@"
{
// Code size 74 (0x4a)
.maxstack 1
.locals init (TEnumerator V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""TEnumerable""
IL_0008: callvirt ""TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_0022
IL_0010: ldloca.s V_0
IL_0012: constrained. ""TEnumerator""
IL_0018: callvirt ""int IMyEnumerator<int>.Current.get""
IL_001d: call ""void System.Console.Write(int)""
IL_0022: ldloca.s V_0
IL_0024: constrained. ""TEnumerator""
IL_002a: callvirt ""bool IMyEnumerator<int>.MoveNext()""
IL_002f: brtrue.s IL_0010
IL_0031: leave.s IL_0049
}
finally
{
IL_0033: ldloc.0
IL_0034: box ""TEnumerator""
IL_0039: brfalse.s IL_0048
IL_003b: ldloca.s V_0
IL_003d: constrained. ""TEnumerator""
IL_0043: callvirt ""void System.IDisposable.Dispose()""
IL_0048: endfinally
}
IL_0049: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()) (OperationKind.Invocation, Type: TEnumerator, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: TEnumerable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: TEnumerable) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean IMyEnumerator<System.Int32>.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 IMyEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean IMyEnumerator<System.Int32>.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 IMyEnumerator<System.Int32>.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void System.IDisposable.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean IMyEnumerator<System.Int32>.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 IMyEnumerator<System.Int32>.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_08(bool s1IsRefStruct, bool addStructConstraintToTEnumerable)
{
var src = @"
interface IMyEnumerator1<T>
{
T Current {get;}
bool MoveNext();
}
interface IMyEnumerator2<T>
{
T Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : IMyEnumerator1<int>, IMyEnumerator2<int>, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IMyEnumerator1<int>, IMyEnumerator2<int>
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : IMyEnumerator1<int>, IMyEnumerator2<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (60,27): error CS0202: foreach requires that the return type 'TEnumerator' of 'IGetEnumerator<TEnumerator>.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property
// foreach (var i in t)
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "t").WithArguments("TEnumerator", "IGetEnumerator<TEnumerator>.GetEnumerator()").WithLocation(60, 27)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
Assert.Null(info.ElementType);
Assert.Null(info.MoveNextMethod);
Assert.Null(info.CurrentProperty);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.Null(op.Info);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumeratorT_09(bool s1IsRefStruct, bool addStructConstraintToTEnumerable)
{
var src = @"
using System.Collections.Generic;
interface IMyEnumerator1<T>
{
T Current {get;}
bool MoveNext();
}
interface IMyEnumerator2<T>
{
T Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : IMyEnumerator1<int>, IMyEnumerator2<int>, IEnumerator<int>, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IMyEnumerator1<int>, IMyEnumerator2<int>, IEnumerator<int>
{
bool stop;
public int Current => 123;
object System.Collections.IEnumerator.Current => Current;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : IMyEnumerator1<int>, IMyEnumerator2<int>, IEnumerator<int>, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (62,27): error CS0202: foreach requires that the return type 'TEnumerator' of 'IGetEnumerator<TEnumerator>.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property
// foreach (var i in t)
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "t").WithArguments("TEnumerator", "IGetEnumerator<TEnumerator>.GetEnumerator()").WithLocation(62, 27)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
Assert.Null(info.ElementType);
Assert.Null(info.MoveNextMethod);
Assert.Null(info.CurrentProperty);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.Null(op.Info);
}
[Theory]
[CombinatorialData]
public void Foreach_IDisposable_01(bool s1IsRefStruct)
{
var src = @"
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : System.IDisposable
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 49 (0x31)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""int S2.Current.get""
IL_0018: call ""void System.Console.Write(int)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: leave.s IL_0030
}
finally
{
IL_0028: ldloca.s V_0
IL_002a: call ""void S2.Dispose()""
IL_002f: endfinally
}
IL_0030: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B5]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 S2.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (1)
IInvocationOperation ( void S2.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B5] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void S2.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
AssertEx.Equal("void S2.Dispose()", op.Info.PatternDisposeMethod.ToTestDisplayString());
Assert.True(op.Info.DisposeArguments.IsEmpty);
}
[Theory]
[CombinatorialData]
public void Foreach_IDisposable_02(bool s1IsRefStruct)
{
var src = @"
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : System.IDisposable
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Dispose()
{
System.Console.Write('D');
}
void System.IDisposable.Dispose() => throw null;
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 49 (0x31)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""int S2.Current.get""
IL_0018: call ""void System.Console.Write(int)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: leave.s IL_0030
}
finally
{
IL_0028: ldloca.s V_0
IL_002a: call ""void S2.Dispose()""
IL_002f: endfinally
}
IL_0030: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B5]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 S2.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (1)
IInvocationOperation ( void S2.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B5] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void S2.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
AssertEx.Equal("void S2.Dispose()", op.Info.PatternDisposeMethod.ToTestDisplayString());
Assert.True(op.Info.DisposeArguments.IsEmpty);
}
[Theory]
[CombinatorialData]
public void Foreach_IDisposable_03(bool s1IsRefStruct)
{
var src = @"
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : System.IDisposable
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
void System.IDisposable.Dispose()
{
System.Console.Write('D');
}
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S3
{
public S4 GetEnumerator()
{
return new S4();
}
}
struct S4 : System.IDisposable
{
bool stop;
public int Current => 456;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
void System.IDisposable.Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test1();
Test2();
}
static void Test1()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
static void Test2()
{
foreach (var i in new S3())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D456D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test1",
@"
{
// Code size 55 (0x37)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""int S2.Current.get""
IL_0018: call ""void System.Console.Write(int)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: leave.s IL_0036
}
finally
{
IL_0028: ldloca.s V_0
IL_002a: constrained. ""S2""
IL_0030: callvirt ""void System.IDisposable.Dispose()""
IL_0035: endfinally
}
IL_0036: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test1").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B5]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 S2.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B5] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().First();
Assert.Equal("new S1()", foreachSyntax.Expression.ToString());
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void System.IDisposable.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IDisposable_04(bool s1IsRefStruct, bool addStructConstraintToTEnumerable)
{
var src = @"
interface ICustomEnumerator
{
int Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, System.IDisposable, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : ICustomEnumerator, System.IDisposable
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : ICustomEnumerator, System.IDisposable, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<TEnumerable, TEnumerator>(TEnumerable)",
@"
{
// Code size 74 (0x4a)
.maxstack 1
.locals init (TEnumerator V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""TEnumerable""
IL_0008: callvirt ""TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_0022
IL_0010: ldloca.s V_0
IL_0012: constrained. ""TEnumerator""
IL_0018: callvirt ""int ICustomEnumerator.Current.get""
IL_001d: call ""void System.Console.Write(int)""
IL_0022: ldloca.s V_0
IL_0024: constrained. ""TEnumerator""
IL_002a: callvirt ""bool ICustomEnumerator.MoveNext()""
IL_002f: brtrue.s IL_0010
IL_0031: leave.s IL_0049
}
finally
{
IL_0033: ldloc.0
IL_0034: box ""TEnumerator""
IL_0039: brfalse.s IL_0048
IL_003b: ldloca.s V_0
IL_003d: constrained. ""TEnumerator""
IL_0043: callvirt ""void System.IDisposable.Dispose()""
IL_0048: endfinally
}
IL_0049: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()) (OperationKind.Invocation, Type: TEnumerator, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: TEnumerable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: TEnumerable) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean ICustomEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 ICustomEnumerator.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean ICustomEnumerator.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 ICustomEnumerator.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void System.IDisposable.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean ICustomEnumerator.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 ICustomEnumerator.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IDisposable_05(bool s1IsRefStruct, bool addStructConstraintToTEnumerable)
{
var src = @"
interface ICustomEnumerator
{
int Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, System.IDisposable, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : ICustomEnumerator, System.IDisposable
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : struct, ICustomEnumerator, System.IDisposable, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<TEnumerable, TEnumerator>(TEnumerable)",
@"
{
// Code size 66 (0x42)
.maxstack 1
.locals init (TEnumerator V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""TEnumerable""
IL_0008: callvirt ""TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_0022
IL_0010: ldloca.s V_0
IL_0012: constrained. ""TEnumerator""
IL_0018: callvirt ""int ICustomEnumerator.Current.get""
IL_001d: call ""void System.Console.Write(int)""
IL_0022: ldloca.s V_0
IL_0024: constrained. ""TEnumerator""
IL_002a: callvirt ""bool ICustomEnumerator.MoveNext()""
IL_002f: brtrue.s IL_0010
IL_0031: leave.s IL_0041
}
finally
{
IL_0033: ldloca.s V_0
IL_0035: constrained. ""TEnumerator""
IL_003b: callvirt ""void System.IDisposable.Dispose()""
IL_0040: endfinally
}
IL_0041: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()) (OperationKind.Invocation, Type: TEnumerator, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: TEnumerable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: TEnumerable) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B5]
IInvocationOperation (virtual System.Boolean ICustomEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 ICustomEnumerator.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: TEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B5] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean ICustomEnumerator.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 ICustomEnumerator.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void System.IDisposable.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean ICustomEnumerator.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 ICustomEnumerator.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IDisposable_06(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
interface ICustomEnumerator
{
int Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
interface IMyDisposable
{
void Dispose();
}
ref struct S2 : ICustomEnumerator, IMyDisposable
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"ICustomEnumerator, IMyDisposable, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (58,27): error CS9247: foreach statement cannot operate on enumerators of type 'TEnumerator' because it is a type parameter that allows ref struct and it is not known at compile time to implement IDisposable.
// foreach (var i in t)
Diagnostic(ErrorCode.ERR_BadAllowByRefLikeEnumerator, "t").WithArguments("TEnumerator").WithLocation(58, 27)
);
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean ICustomEnumerator.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 ICustomEnumerator.Current { get; }", info.CurrentProperty.ToTestDisplayString());
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean ICustomEnumerator.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 ICustomEnumerator.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.False(op.Info.NeedsDispose);
Assert.False(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IDisposable_07(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
interface ICustomEnumerator
{
int Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : ICustomEnumerator, System.IDisposable
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"ICustomEnumerator, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (53,27): error CS9247: foreach statement cannot operate on enumerators of type 'TEnumerator' because it is a type parameter that allows ref struct and it is not known at compile time to implement IDisposable.
// foreach (var i in t)
Diagnostic(ErrorCode.ERR_BadAllowByRefLikeEnumerator, "t").WithArguments("TEnumerator").WithLocation(53, 27)
);
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean ICustomEnumerator.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32 ICustomEnumerator.Current { get; }", info.CurrentProperty.ToTestDisplayString());
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean ICustomEnumerator.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Int32 ICustomEnumerator.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.False(op.Info.NeedsDispose);
Assert.False(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Fact]
public void Foreach_IDisposable_LanguageVersion_01()
{
var src1 = @"
public struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
public ref struct S2 : System.IDisposable
{
public int Current => throw null;
public bool MoveNext() => throw null;
public void Dispose() => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Main()
{
foreach (var i in new S1())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IDisposable_LanguageVersion_03()
{
var src1 = @"
public struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
public ref struct S2 : System.IDisposable
{
public int Current => throw null;
public bool MoveNext() => throw null;
void System.IDisposable.Dispose() => throw null;
}
public struct S3
{
public S4 GetEnumerator()
{
return new S4();
}
}
public struct S4 : System.IDisposable
{
public int Current => throw null;
public bool MoveNext() => throw null;
void System.IDisposable.Dispose() => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Main()
{
foreach (var i in new S1()) {}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (6,9): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// foreach (var i in new S1()) {}
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach (var i in new S1()) {}").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 9)
);
comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (10,24): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// public ref struct S2 : System.IDisposable
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "System.IDisposable").WithArguments("ref struct interfaces", "13.0").WithLocation(10, 24)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
var src3 = @"
class C
{
static void Main()
{
foreach (var i in new S3()) {}
}
}
";
var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp3.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IDisposable_LanguageVersion_04()
{
var src = @"
interface ICustomEnumerator
{
int Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, System.IDisposable, allows ref struct
{
TEnumerator GetEnumerator();
}
class C
{
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : ICustomEnumerator, System.IDisposable, allows ref struct
{
foreach (var i in t) {}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp.VerifyEmitDiagnostics(
// (8,105): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, System.IDisposable, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(8, 105),
// (16,65): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where TEnumerable : IGetEnumerator<TEnumerator>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(16, 65),
// (17,75): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where TEnumerator : ICustomEnumerator, System.IDisposable, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(17, 75)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics();
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_Pattern_01()
{
var src = @"
ref struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void Foreach_Pattern_02()
{
var src = @"
struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2
{
bool stop;
public int Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void AwaitUsing_01()
{
var src1 = @"
using System;
using System.Threading.Tasks;
ref struct S2 : IAsyncDisposable
{
public ValueTask DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await using (new S2())
{
System.Console.Write(123);
}
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Main>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext",
@"
{
// Code size 229 (0xe5)
.maxstack 3
.locals init (int V_0,
S2 V_1,
object V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter V_3,
System.Threading.Tasks.ValueTask V_4,
System.Exception V_5)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Main>d__0.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_006d
IL_000a: ldloca.s V_1
IL_000c: initobj ""S2""
IL_0012: ldarg.0
IL_0013: ldnull
IL_0014: stfld ""object C.<Main>d__0.<>7__wrap1""
IL_0019: ldarg.0
IL_001a: ldc.i4.0
IL_001b: stfld ""int C.<Main>d__0.<>7__wrap2""
.try
{
IL_0020: ldc.i4.s 123
IL_0022: call ""void System.Console.Write(int)""
IL_0027: leave.s IL_0033
}
catch object
{
IL_0029: stloc.2
IL_002a: ldarg.0
IL_002b: ldloc.2
IL_002c: stfld ""object C.<Main>d__0.<>7__wrap1""
IL_0031: leave.s IL_0033
}
IL_0033: ldloca.s V_1
IL_0035: call ""System.Threading.Tasks.ValueTask S2.DisposeAsync()""
IL_003a: stloc.s V_4
IL_003c: ldloca.s V_4
IL_003e: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_0043: stloc.3
IL_0044: ldloca.s V_3
IL_0046: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_004b: brtrue.s IL_0089
IL_004d: ldarg.0
IL_004e: ldc.i4.0
IL_004f: dup
IL_0050: stloc.0
IL_0051: stfld ""int C.<Main>d__0.<>1__state""
IL_0056: ldarg.0
IL_0057: ldloc.3
IL_0058: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__1""
IL_005d: ldarg.0
IL_005e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0063: ldloca.s V_3
IL_0065: ldarg.0
IL_0066: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Main>d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Main>d__0)""
IL_006b: leave.s IL_00e4
IL_006d: ldarg.0
IL_006e: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__1""
IL_0073: stloc.3
IL_0074: ldarg.0
IL_0075: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__1""
IL_007a: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_0080: ldarg.0
IL_0081: ldc.i4.m1
IL_0082: dup
IL_0083: stloc.0
IL_0084: stfld ""int C.<Main>d__0.<>1__state""
IL_0089: ldloca.s V_3
IL_008b: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_0090: ldarg.0
IL_0091: ldfld ""object C.<Main>d__0.<>7__wrap1""
IL_0096: stloc.2
IL_0097: ldloc.2
IL_0098: brfalse.s IL_00af
IL_009a: ldloc.2
IL_009b: isinst ""System.Exception""
IL_00a0: dup
IL_00a1: brtrue.s IL_00a5
IL_00a3: ldloc.2
IL_00a4: throw
IL_00a5: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_00aa: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_00af: ldarg.0
IL_00b0: ldnull
IL_00b1: stfld ""object C.<Main>d__0.<>7__wrap1""
IL_00b6: leave.s IL_00d1
}
catch System.Exception
{
IL_00b8: stloc.s V_5
IL_00ba: ldarg.0
IL_00bb: ldc.i4.s -2
IL_00bd: stfld ""int C.<Main>d__0.<>1__state""
IL_00c2: ldarg.0
IL_00c3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_00c8: ldloc.s V_5
IL_00ca: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_00cf: leave.s IL_00e4
}
IL_00d1: ldarg.0
IL_00d2: ldc.i4.s -2
IL_00d4: stfld ""int C.<Main>d__0.<>1__state""
IL_00d9: ldarg.0
IL_00da: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_00df: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_00e4: ret
}
");
var tree = comp1.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp1, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S2()')
Value:
IObjectCreationOperation (Constructor: S2..ctor()) (OperationKind.ObjectCreation, Type: S2) (Syntax: 'new S2()')
Arguments(0)
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B4]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new S2()')
Expression:
IInvocationOperation ( System.Threading.Tasks.ValueTask S2.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new S2()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S2()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var src2 = @"
using System;
using System.Threading.Tasks;
ref struct S2 : IAsyncDisposable
{
public ValueTask DisposeAsync()
{
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await using (new S2())
{
await Task.Yield();
}
}
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp2.VerifyEmitDiagnostics(
// (17,22): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await using (new S2())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new S2()").WithArguments("S2").WithLocation(17, 22)
);
}
[Fact]
public void AwaitUsing_02()
{
var src1 = @"
using System;
using System.Threading.Tasks;
ref struct S2 : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
struct S3 : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await Test1();
await Test2();
}
static async Task Test1()
{
await using (new S2())
{
System.Console.Write(123);
}
}
static async Task Test2()
{
await using (new S3())
{
System.Console.Write(456);
}
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D456D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Test1>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext",
@"
{
// Code size 235 (0xeb)
.maxstack 3
.locals init (int V_0,
S2 V_1,
object V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter V_3,
System.Threading.Tasks.ValueTask V_4,
System.Exception V_5)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Test1>d__1.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0073
IL_000a: ldloca.s V_1
IL_000c: initobj ""S2""
IL_0012: ldarg.0
IL_0013: ldnull
IL_0014: stfld ""object C.<Test1>d__1.<>7__wrap1""
IL_0019: ldarg.0
IL_001a: ldc.i4.0
IL_001b: stfld ""int C.<Test1>d__1.<>7__wrap2""
.try
{
IL_0020: ldc.i4.s 123
IL_0022: call ""void System.Console.Write(int)""
IL_0027: leave.s IL_0033
}
catch object
{
IL_0029: stloc.2
IL_002a: ldarg.0
IL_002b: ldloc.2
IL_002c: stfld ""object C.<Test1>d__1.<>7__wrap1""
IL_0031: leave.s IL_0033
}
IL_0033: ldloca.s V_1
IL_0035: constrained. ""S2""
IL_003b: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_0040: stloc.s V_4
IL_0042: ldloca.s V_4
IL_0044: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_0049: stloc.3
IL_004a: ldloca.s V_3
IL_004c: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_0051: brtrue.s IL_008f
IL_0053: ldarg.0
IL_0054: ldc.i4.0
IL_0055: dup
IL_0056: stloc.0
IL_0057: stfld ""int C.<Test1>d__1.<>1__state""
IL_005c: ldarg.0
IL_005d: ldloc.3
IL_005e: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test1>d__1.<>u__1""
IL_0063: ldarg.0
IL_0064: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test1>d__1.<>t__builder""
IL_0069: ldloca.s V_3
IL_006b: ldarg.0
IL_006c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Test1>d__1>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Test1>d__1)""
IL_0071: leave.s IL_00ea
IL_0073: ldarg.0
IL_0074: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test1>d__1.<>u__1""
IL_0079: stloc.3
IL_007a: ldarg.0
IL_007b: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test1>d__1.<>u__1""
IL_0080: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_0086: ldarg.0
IL_0087: ldc.i4.m1
IL_0088: dup
IL_0089: stloc.0
IL_008a: stfld ""int C.<Test1>d__1.<>1__state""
IL_008f: ldloca.s V_3
IL_0091: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_0096: ldarg.0
IL_0097: ldfld ""object C.<Test1>d__1.<>7__wrap1""
IL_009c: stloc.2
IL_009d: ldloc.2
IL_009e: brfalse.s IL_00b5
IL_00a0: ldloc.2
IL_00a1: isinst ""System.Exception""
IL_00a6: dup
IL_00a7: brtrue.s IL_00ab
IL_00a9: ldloc.2
IL_00aa: throw
IL_00ab: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_00b0: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_00b5: ldarg.0
IL_00b6: ldnull
IL_00b7: stfld ""object C.<Test1>d__1.<>7__wrap1""
IL_00bc: leave.s IL_00d7
}
catch System.Exception
{
IL_00be: stloc.s V_5
IL_00c0: ldarg.0
IL_00c1: ldc.i4.s -2
IL_00c3: stfld ""int C.<Test1>d__1.<>1__state""
IL_00c8: ldarg.0
IL_00c9: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test1>d__1.<>t__builder""
IL_00ce: ldloc.s V_5
IL_00d0: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_00d5: leave.s IL_00ea
}
IL_00d7: ldarg.0
IL_00d8: ldc.i4.s -2
IL_00da: stfld ""int C.<Test1>d__1.<>1__state""
IL_00df: ldarg.0
IL_00e0: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test1>d__1.<>t__builder""
IL_00e5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_00ea: ret
}
");
var tree = comp1.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test1").Single();
VerifyFlowGraph(comp1, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S2()')
Value:
IObjectCreationOperation (Constructor: S2..ctor()) (OperationKind.ObjectCreation, Type: S2) (Syntax: 'new S2()')
Arguments(0)
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B4]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new S2()')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new S2()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S2()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var src2 = @"
using System;
using System.Threading.Tasks;
ref struct S2 : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync()
{
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await using (new S2())
{
await Task.Yield();
}
}
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp2.VerifyEmitDiagnostics(
// (17,22): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await using (new S2())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new S2()").WithArguments("S2").WithLocation(17, 22)
);
}
[Fact]
public void AwaitUsing_03()
{
var src1 = @"
using System;
using System.Threading.Tasks;
ref struct S2 : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync() => throw null;
public ValueTask DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await using (new S2())
{
System.Console.Write(123);
}
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Main>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext",
@"
{
// Code size 229 (0xe5)
.maxstack 3
.locals init (int V_0,
S2 V_1,
object V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter V_3,
System.Threading.Tasks.ValueTask V_4,
System.Exception V_5)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Main>d__0.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_006d
IL_000a: ldloca.s V_1
IL_000c: initobj ""S2""
IL_0012: ldarg.0
IL_0013: ldnull
IL_0014: stfld ""object C.<Main>d__0.<>7__wrap1""
IL_0019: ldarg.0
IL_001a: ldc.i4.0
IL_001b: stfld ""int C.<Main>d__0.<>7__wrap2""
.try
{
IL_0020: ldc.i4.s 123
IL_0022: call ""void System.Console.Write(int)""
IL_0027: leave.s IL_0033
}
catch object
{
IL_0029: stloc.2
IL_002a: ldarg.0
IL_002b: ldloc.2
IL_002c: stfld ""object C.<Main>d__0.<>7__wrap1""
IL_0031: leave.s IL_0033
}
IL_0033: ldloca.s V_1
IL_0035: call ""System.Threading.Tasks.ValueTask S2.DisposeAsync()""
IL_003a: stloc.s V_4
IL_003c: ldloca.s V_4
IL_003e: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_0043: stloc.3
IL_0044: ldloca.s V_3
IL_0046: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_004b: brtrue.s IL_0089
IL_004d: ldarg.0
IL_004e: ldc.i4.0
IL_004f: dup
IL_0050: stloc.0
IL_0051: stfld ""int C.<Main>d__0.<>1__state""
IL_0056: ldarg.0
IL_0057: ldloc.3
IL_0058: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__1""
IL_005d: ldarg.0
IL_005e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0063: ldloca.s V_3
IL_0065: ldarg.0
IL_0066: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Main>d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Main>d__0)""
IL_006b: leave.s IL_00e4
IL_006d: ldarg.0
IL_006e: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__1""
IL_0073: stloc.3
IL_0074: ldarg.0
IL_0075: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__1""
IL_007a: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_0080: ldarg.0
IL_0081: ldc.i4.m1
IL_0082: dup
IL_0083: stloc.0
IL_0084: stfld ""int C.<Main>d__0.<>1__state""
IL_0089: ldloca.s V_3
IL_008b: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_0090: ldarg.0
IL_0091: ldfld ""object C.<Main>d__0.<>7__wrap1""
IL_0096: stloc.2
IL_0097: ldloc.2
IL_0098: brfalse.s IL_00af
IL_009a: ldloc.2
IL_009b: isinst ""System.Exception""
IL_00a0: dup
IL_00a1: brtrue.s IL_00a5
IL_00a3: ldloc.2
IL_00a4: throw
IL_00a5: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_00aa: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_00af: ldarg.0
IL_00b0: ldnull
IL_00b1: stfld ""object C.<Main>d__0.<>7__wrap1""
IL_00b6: leave.s IL_00d1
}
catch System.Exception
{
IL_00b8: stloc.s V_5
IL_00ba: ldarg.0
IL_00bb: ldc.i4.s -2
IL_00bd: stfld ""int C.<Main>d__0.<>1__state""
IL_00c2: ldarg.0
IL_00c3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_00c8: ldloc.s V_5
IL_00ca: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_00cf: leave.s IL_00e4
}
IL_00d1: ldarg.0
IL_00d2: ldc.i4.s -2
IL_00d4: stfld ""int C.<Main>d__0.<>1__state""
IL_00d9: ldarg.0
IL_00da: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_00df: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_00e4: ret
}
");
var tree = comp1.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp1, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S2()')
Value:
IObjectCreationOperation (Constructor: S2..ctor()) (OperationKind.ObjectCreation, Type: S2) (Syntax: 'new S2()')
Arguments(0)
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B4]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new S2()')
Expression:
IInvocationOperation ( System.Threading.Tasks.ValueTask S2.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new S2()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S2()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var src2 = @"
using System;
using System.Threading.Tasks;
ref struct S2 : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync() => throw null;
public ValueTask DisposeAsync()
{
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await using (new S2())
{
await Task.Yield();
}
}
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp2.VerifyEmitDiagnostics(
// (19,22): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await using (new S2())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new S2()").WithArguments("S2").WithLocation(19, 22)
);
}
[Fact]
public void AwaitUsing_04()
{
var src1 = @"
using System;
using System.Threading.Tasks;
ref struct S2 : IAsyncDisposable
{
public ValueTask DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await Test<S2>();
}
static async Task Test<T>() where T : IAsyncDisposable, new(), allows ref struct
{
await using (new T())
{
System.Console.Write(123);
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Test>d__1<T>.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext",
@"
{
// Code size 241 (0xf1)
.maxstack 3
.locals init (int V_0,
T V_1,
object V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter V_3,
System.Threading.Tasks.ValueTask V_4,
System.Exception V_5)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Test>d__1<T>.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0079
IL_000a: call ""T System.Activator.CreateInstance<T>()""
IL_000f: stloc.1
IL_0010: ldarg.0
IL_0011: ldnull
IL_0012: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_0017: ldarg.0
IL_0018: ldc.i4.0
IL_0019: stfld ""int C.<Test>d__1<T>.<>7__wrap2""
.try
{
IL_001e: ldc.i4.s 123
IL_0020: call ""void System.Console.Write(int)""
IL_0025: leave.s IL_0031
}
catch object
{
IL_0027: stloc.2
IL_0028: ldarg.0
IL_0029: ldloc.2
IL_002a: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_002f: leave.s IL_0031
}
IL_0031: ldloc.1
IL_0032: box ""T""
IL_0037: brfalse.s IL_009c
IL_0039: ldloca.s V_1
IL_003b: constrained. ""T""
IL_0041: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_0046: stloc.s V_4
IL_0048: ldloca.s V_4
IL_004a: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_004f: stloc.3
IL_0050: ldloca.s V_3
IL_0052: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_0057: brtrue.s IL_0095
IL_0059: ldarg.0
IL_005a: ldc.i4.0
IL_005b: dup
IL_005c: stloc.0
IL_005d: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0062: ldarg.0
IL_0063: ldloc.3
IL_0064: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_0069: ldarg.0
IL_006a: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_006f: ldloca.s V_3
IL_0071: ldarg.0
IL_0072: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Test>d__1<T>)""
IL_0077: leave.s IL_00f0
IL_0079: ldarg.0
IL_007a: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_007f: stloc.3
IL_0080: ldarg.0
IL_0081: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_0086: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_008c: ldarg.0
IL_008d: ldc.i4.m1
IL_008e: dup
IL_008f: stloc.0
IL_0090: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0095: ldloca.s V_3
IL_0097: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_009c: ldarg.0
IL_009d: ldfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_00a2: stloc.2
IL_00a3: ldloc.2
IL_00a4: brfalse.s IL_00bb
IL_00a6: ldloc.2
IL_00a7: isinst ""System.Exception""
IL_00ac: dup
IL_00ad: brtrue.s IL_00b1
IL_00af: ldloc.2
IL_00b0: throw
IL_00b1: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_00b6: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_00bb: ldarg.0
IL_00bc: ldnull
IL_00bd: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_00c2: leave.s IL_00dd
}
catch System.Exception
{
IL_00c4: stloc.s V_5
IL_00c6: ldarg.0
IL_00c7: ldc.i4.s -2
IL_00c9: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00ce: ldarg.0
IL_00cf: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_00d4: ldloc.s V_5
IL_00d6: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_00db: leave.s IL_00f0
}
IL_00dd: ldarg.0
IL_00de: ldc.i4.s -2
IL_00e0: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00e5: ldarg.0
IL_00e6: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_00eb: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_00f0: ret
}
");
var tree = comp1.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp1, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new T()')
Value:
ITypeParameterObjectCreationOperation (OperationKind.TypeParameterObjectCreation, Type: T) (Syntax: 'new T()')
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B6]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B5]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new T()')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: 'new T()')
Next (Regular) Block[B4]
Block[B4] - Block
Predecessors: [B3]
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new T()')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new T()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: 'new T()')
Arguments(0)
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B3] [B4]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B6] - Exit
Predecessors: [B2]
Statements (0)
""");
var src2 = @"
using System;
using System.Threading.Tasks;
class C
{
static async Task Test<T>() where T : IAsyncDisposable, new(), allows ref struct
{
await using (new T())
{
await Task.Yield();
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics(
// (9,22): error CS4007: Instance of type 'T' cannot be preserved across 'await' or 'yield' boundary.
// await using (new T())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new T()").WithArguments("T").WithLocation(9, 22)
);
}
[Fact]
public void AwaitUsing_05()
{
var src1 = @"
using System;
using System.Threading.Tasks;
ref struct S2 : IAsyncDisposable
{
public ValueTask DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await Test<S2>();
}
static async Task Test<T>() where T : struct, IAsyncDisposable, allows ref struct
{
await using (new T())
{
System.Console.Write(123);
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Test>d__1<T>.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext",
@"
{
// Code size 233 (0xe9)
.maxstack 3
.locals init (int V_0,
T V_1,
object V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter V_3,
System.Threading.Tasks.ValueTask V_4,
System.Exception V_5)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Test>d__1<T>.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0071
IL_000a: call ""T System.Activator.CreateInstance<T>()""
IL_000f: stloc.1
IL_0010: ldarg.0
IL_0011: ldnull
IL_0012: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_0017: ldarg.0
IL_0018: ldc.i4.0
IL_0019: stfld ""int C.<Test>d__1<T>.<>7__wrap2""
.try
{
IL_001e: ldc.i4.s 123
IL_0020: call ""void System.Console.Write(int)""
IL_0025: leave.s IL_0031
}
catch object
{
IL_0027: stloc.2
IL_0028: ldarg.0
IL_0029: ldloc.2
IL_002a: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_002f: leave.s IL_0031
}
IL_0031: ldloca.s V_1
IL_0033: constrained. ""T""
IL_0039: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_003e: stloc.s V_4
IL_0040: ldloca.s V_4
IL_0042: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_0047: stloc.3
IL_0048: ldloca.s V_3
IL_004a: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_004f: brtrue.s IL_008d
IL_0051: ldarg.0
IL_0052: ldc.i4.0
IL_0053: dup
IL_0054: stloc.0
IL_0055: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_005a: ldarg.0
IL_005b: ldloc.3
IL_005c: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_0061: ldarg.0
IL_0062: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_0067: ldloca.s V_3
IL_0069: ldarg.0
IL_006a: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Test>d__1<T>)""
IL_006f: leave.s IL_00e8
IL_0071: ldarg.0
IL_0072: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_0077: stloc.3
IL_0078: ldarg.0
IL_0079: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_007e: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_0084: ldarg.0
IL_0085: ldc.i4.m1
IL_0086: dup
IL_0087: stloc.0
IL_0088: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_008d: ldloca.s V_3
IL_008f: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_0094: ldarg.0
IL_0095: ldfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_009a: stloc.2
IL_009b: ldloc.2
IL_009c: brfalse.s IL_00b3
IL_009e: ldloc.2
IL_009f: isinst ""System.Exception""
IL_00a4: dup
IL_00a5: brtrue.s IL_00a9
IL_00a7: ldloc.2
IL_00a8: throw
IL_00a9: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_00ae: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_00b3: ldarg.0
IL_00b4: ldnull
IL_00b5: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_00ba: leave.s IL_00d5
}
catch System.Exception
{
IL_00bc: stloc.s V_5
IL_00be: ldarg.0
IL_00bf: ldc.i4.s -2
IL_00c1: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00c6: ldarg.0
IL_00c7: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_00cc: ldloc.s V_5
IL_00ce: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_00d3: leave.s IL_00e8
}
IL_00d5: ldarg.0
IL_00d6: ldc.i4.s -2
IL_00d8: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00dd: ldarg.0
IL_00de: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_00e3: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_00e8: ret
}
");
var tree = comp1.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp1, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new T()')
Value:
ITypeParameterObjectCreationOperation (OperationKind.TypeParameterObjectCreation, Type: T) (Syntax: 'new T()')
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B4]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new T()')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new T()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: 'new T()')
Arguments(0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var src2 = @"
using System;
using System.Threading.Tasks;
class C
{
static async Task Test<T>() where T : struct, IAsyncDisposable, allows ref struct
{
await using (new T())
{
await Task.Yield();
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics(
// (9,22): error CS4007: Instance of type 'T' cannot be preserved across 'await' or 'yield' boundary.
// await using (new T())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new T()").WithArguments("T").WithLocation(9, 22)
);
}
[Fact]
public void AwaitUsing_06()
{
var src1 = @"
using System.Threading.Tasks;
interface IMyAsyncDisposable
{
ValueTask DisposeAsync();
}
ref struct S2 : IMyAsyncDisposable
{
public ValueTask DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await Test<S2>();
}
static async Task Test<T>() where T : IMyAsyncDisposable, new(), allows ref struct
{
await using (new T())
{
System.Console.Write(123);
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.Passes :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Test>d__1<T>.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext",
@"
{
// Code size 241 (0xf1)
.maxstack 3
.locals init (int V_0,
T V_1,
object V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter V_3,
System.Threading.Tasks.ValueTask V_4,
System.Exception V_5)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Test>d__1<T>.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0079
IL_000a: call ""T System.Activator.CreateInstance<T>()""
IL_000f: stloc.1
IL_0010: ldarg.0
IL_0011: ldnull
IL_0012: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_0017: ldarg.0
IL_0018: ldc.i4.0
IL_0019: stfld ""int C.<Test>d__1<T>.<>7__wrap2""
.try
{
IL_001e: ldc.i4.s 123
IL_0020: call ""void System.Console.Write(int)""
IL_0025: leave.s IL_0031
}
catch object
{
IL_0027: stloc.2
IL_0028: ldarg.0
IL_0029: ldloc.2
IL_002a: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_002f: leave.s IL_0031
}
IL_0031: ldloc.1
IL_0032: box ""T""
IL_0037: brfalse.s IL_009c
IL_0039: ldloca.s V_1
IL_003b: constrained. ""T""
IL_0041: callvirt ""System.Threading.Tasks.ValueTask IMyAsyncDisposable.DisposeAsync()""
IL_0046: stloc.s V_4
IL_0048: ldloca.s V_4
IL_004a: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_004f: stloc.3
IL_0050: ldloca.s V_3
IL_0052: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_0057: brtrue.s IL_0095
IL_0059: ldarg.0
IL_005a: ldc.i4.0
IL_005b: dup
IL_005c: stloc.0
IL_005d: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0062: ldarg.0
IL_0063: ldloc.3
IL_0064: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_0069: ldarg.0
IL_006a: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_006f: ldloca.s V_3
IL_0071: ldarg.0
IL_0072: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Test>d__1<T>)""
IL_0077: leave.s IL_00f0
IL_0079: ldarg.0
IL_007a: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_007f: stloc.3
IL_0080: ldarg.0
IL_0081: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__1""
IL_0086: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_008c: ldarg.0
IL_008d: ldc.i4.m1
IL_008e: dup
IL_008f: stloc.0
IL_0090: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0095: ldloca.s V_3
IL_0097: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_009c: ldarg.0
IL_009d: ldfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_00a2: stloc.2
IL_00a3: ldloc.2
IL_00a4: brfalse.s IL_00bb
IL_00a6: ldloc.2
IL_00a7: isinst ""System.Exception""
IL_00ac: dup
IL_00ad: brtrue.s IL_00b1
IL_00af: ldloc.2
IL_00b0: throw
IL_00b1: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_00b6: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_00bb: ldarg.0
IL_00bc: ldnull
IL_00bd: stfld ""object C.<Test>d__1<T>.<>7__wrap1""
IL_00c2: leave.s IL_00dd
}
catch System.Exception
{
IL_00c4: stloc.s V_5
IL_00c6: ldarg.0
IL_00c7: ldc.i4.s -2
IL_00c9: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00ce: ldarg.0
IL_00cf: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_00d4: ldloc.s V_5
IL_00d6: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_00db: leave.s IL_00f0
}
IL_00dd: ldarg.0
IL_00de: ldc.i4.s -2
IL_00e0: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00e5: ldarg.0
IL_00e6: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_00eb: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_00f0: ret
}
");
var tree = comp1.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp1, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new T()')
Value:
ITypeParameterObjectCreationOperation (OperationKind.TypeParameterObjectCreation, Type: T) (Syntax: 'new T()')
Initializer:
null
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Cons ... Write(123);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Cons ... .Write(123)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '123')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 123) (Syntax: '123')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B6]
Finalizing: {R4}
Leaving: {R3} {R2} {R1}
}
.finally {R4}
{
Block[B3] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B5]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new T()')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: 'new T()')
Next (Regular) Block[B4]
Block[B4] - Block
Predecessors: [B3]
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new T()')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask IMyAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new T()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: 'new T()')
Arguments(0)
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B3] [B4]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B6] - Exit
Predecessors: [B2]
Statements (0)
""");
var src2 = @"
using System.Threading.Tasks;
interface IMyAsyncDisposable
{
ValueTask DisposeAsync();
}
class C
{
static async Task Test<T>() where T : IMyAsyncDisposable, new(), allows ref struct
{
await using (new T())
{
await Task.Yield();
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics(
// (13,22): error CS4007: Instance of type 'T' cannot be preserved across 'await' or 'yield' boundary.
// await using (new T())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new T()").WithArguments("T").WithLocation(13, 22)
);
}
[Fact]
public void AwaitUsing_07()
{
var src = @"
using System.Threading.Tasks;
interface IMyAsyncDisposable1
{
ValueTask DisposeAsync();
}
interface IMyAsyncDisposable2
{
ValueTask DisposeAsync();
}
ref struct S2 : IMyAsyncDisposable1, IMyAsyncDisposable2
{
public ValueTask DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await Test<S2>();
}
static async Task Test<T>() where T : IMyAsyncDisposable1, IMyAsyncDisposable2, new(), allows ref struct
{
await using (new T())
{
System.Console.Write(123);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (32,22): error CS8410: 'T': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method.
// await using (new T())
Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "new T()").WithArguments("T").WithLocation(32, 22)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/72819")]
public void AwaitUsing_08()
{
// 'System.Activator.CreateInstance<T>' will be changed to include 'allows ref struct' constraint,
// see https://github.com/dotnet/runtime/issues/65112.
var src = @"
using System;
using System.Threading.Tasks;
interface IMyAsyncDisposable1
{
ValueTask DisposeAsync();
}
interface IMyAsyncDisposable2
{
ValueTask DisposeAsync();
}
ref struct S2 : IMyAsyncDisposable1, IMyAsyncDisposable2, IAsyncDisposable
{
ValueTask IMyAsyncDisposable1.DisposeAsync() => throw null;
ValueTask IMyAsyncDisposable2.DisposeAsync() => throw null;
public ValueTask DisposeAsync()
{
System.Console.Write('D');
return ValueTask.CompletedTask;
}
}
class C
{
static async Task Main()
{
await Test<S2>();
}
static async Task Test<T>() where T : IMyAsyncDisposable1, IMyAsyncDisposable2, IAsyncDisposable, new(), allows ref struct
{
await using (new T())
{
System.Console.Write(123);
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/73563")]
public void AwaitUsing_LanguageVersion_01()
{
var src1 = @"
using System;
using System.Threading.Tasks;
public ref struct S2 : IAsyncDisposable
{
public ValueTask DisposeAsync()
{
return ValueTask.CompletedTask;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static async System.Threading.Tasks.Task Main()
{
await using (new S2())
{
}
await using (var s = new S2())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (6,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new S2()").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 22),
// (10,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (var s = new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(10, 22)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitUsing_LanguageVersion_02()
{
var src1 = @"
using System;
using System.Threading.Tasks;
public ref struct S2 : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync()
{
return ValueTask.CompletedTask;
}
}
public struct S3 : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync()
{
return ValueTask.CompletedTask;
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static async System.Threading.Tasks.Task Main()
{
await using (new S2())
{
}
await using (var s = new S2())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (6,22): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new S2()").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 22),
// (6,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new S2()").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 22),
// (10,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (var s = new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(10, 22),
// (10,22): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (var s = new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var s = new S2()").WithArguments("ref struct interfaces", "13.0").WithLocation(10, 22)
);
comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (5,24): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// public ref struct S2 : IAsyncDisposable
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "IAsyncDisposable").WithArguments("ref struct interfaces", "13.0").WithLocation(5, 24),
// (25,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new S2()").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(25, 22),
// (29,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (var s = new S2())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(29, 22)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
var src3 = @"
class C
{
static async System.Threading.Tasks.Task Main()
{
await using (new S3())
{
}
await using (var s = new S3())
{
}
}
}
";
var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp3.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitUsing_LanguageVersion_04()
{
var src = @"
using System;
using System.Threading.Tasks;
class C
{
static async Task Test<T>() where T : IAsyncDisposable, new(), allows ref struct
{
await using (new T())
{
}
await using (var s = new T())
{
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp.VerifyEmitDiagnostics(
// (7,75): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static async Task Test<T>() where T : IAsyncDisposable, new(), allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(7, 75),
// (9,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (new T())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new T()").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(9, 22),
// (13,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (var s = new T())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(13, 22),
// (23,63): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public static T CreateInstance<T>() where T : allows ref struct => default;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(23, 63)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics();
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitUsing_LanguageVersion_06()
{
var src = @"
using System.Threading.Tasks;
class C
{
static async Task Test<T>() where T : IMyAsyncDisposable, new(), allows ref struct
{
await using (new T())
{
}
await using (var s = new T())
{
}
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
interface IMyAsyncDisposable
{
ValueTask DisposeAsync();
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp.VerifyEmitDiagnostics(
// (7,77): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static async Task Test<T>() where T : IMyAsyncDisposable, new(), allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(7, 77),
// (9,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (new T())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new T()").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(9, 22),
// (13,22): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await using (var s = new T())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(13, 22),
// (23,63): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public static T CreateInstance<T>() where T : allows ref struct => default;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(23, 63)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics();
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitForeach_IAsyncEnumerableT_01()
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
ref struct S : IAsyncEnumerable<int>
{
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken token = default)
{
return Get123();
}
async static IAsyncEnumerator<int> Get123()
{
await Task.Yield();
yield return 123;
}
}
class C
{
static async Task Main()
{
await foreach (var i in new S())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Main>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()",
@"
{
// Code size 405 (0x195)
.maxstack 3
.locals init (int V_0,
S V_1,
System.Threading.CancellationToken V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter<bool> V_3,
System.Threading.Tasks.ValueTask<bool> V_4,
object V_5,
System.Runtime.CompilerServices.ValueTaskAwaiter V_6,
System.Threading.Tasks.ValueTask V_7,
System.Exception V_8)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Main>d__0.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_003c
IL_000a: ldloc.0
IL_000b: ldc.i4.1
IL_000c: beq IL_0111
IL_0011: ldarg.0
IL_0012: ldloca.s V_1
IL_0014: dup
IL_0015: initobj ""S""
IL_001b: ldloca.s V_2
IL_001d: initobj ""System.Threading.CancellationToken""
IL_0023: ldloc.2
IL_0024: call ""System.Collections.Generic.IAsyncEnumerator<int> S.GetAsyncEnumerator(System.Threading.CancellationToken)""
IL_0029: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_002e: ldarg.0
IL_002f: ldnull
IL_0030: stfld ""object C.<Main>d__0.<>7__wrap2""
IL_0035: ldarg.0
IL_0036: ldc.i4.0
IL_0037: stfld ""int C.<Main>d__0.<>7__wrap3""
IL_003c: nop
.try
{
IL_003d: ldloc.0
IL_003e: brfalse.s IL_0093
IL_0040: br.s IL_0052
IL_0042: ldarg.0
IL_0043: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_0048: callvirt ""int System.Collections.Generic.IAsyncEnumerator<int>.Current.get""
IL_004d: call ""void System.Console.Write(int)""
IL_0052: ldarg.0
IL_0053: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_0058: callvirt ""System.Threading.Tasks.ValueTask<bool> System.Collections.Generic.IAsyncEnumerator<int>.MoveNextAsync()""
IL_005d: stloc.s V_4
IL_005f: ldloca.s V_4
IL_0061: call ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> System.Threading.Tasks.ValueTask<bool>.GetAwaiter()""
IL_0066: stloc.3
IL_0067: ldloca.s V_3
IL_0069: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.IsCompleted.get""
IL_006e: brtrue.s IL_00af
IL_0070: ldarg.0
IL_0071: ldc.i4.0
IL_0072: dup
IL_0073: stloc.0
IL_0074: stfld ""int C.<Main>d__0.<>1__state""
IL_0079: ldarg.0
IL_007a: ldloc.3
IL_007b: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_0080: ldarg.0
IL_0081: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0086: ldloca.s V_3
IL_0088: ldarg.0
IL_0089: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, C.<Main>d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, ref C.<Main>d__0)""
IL_008e: leave IL_0194
IL_0093: ldarg.0
IL_0094: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_0099: stloc.3
IL_009a: ldarg.0
IL_009b: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_00a0: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool>""
IL_00a6: ldarg.0
IL_00a7: ldc.i4.m1
IL_00a8: dup
IL_00a9: stloc.0
IL_00aa: stfld ""int C.<Main>d__0.<>1__state""
IL_00af: ldloca.s V_3
IL_00b1: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.GetResult()""
IL_00b6: brtrue.s IL_0042
IL_00b8: leave.s IL_00c6
}
catch object
{
IL_00ba: stloc.s V_5
IL_00bc: ldarg.0
IL_00bd: ldloc.s V_5
IL_00bf: stfld ""object C.<Main>d__0.<>7__wrap2""
IL_00c4: leave.s IL_00c6
}
IL_00c6: ldarg.0
IL_00c7: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_00cc: brfalse.s IL_0135
IL_00ce: ldarg.0
IL_00cf: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_00d4: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_00d9: stloc.s V_7
IL_00db: ldloca.s V_7
IL_00dd: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_00e2: stloc.s V_6
IL_00e4: ldloca.s V_6
IL_00e6: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_00eb: brtrue.s IL_012e
IL_00ed: ldarg.0
IL_00ee: ldc.i4.1
IL_00ef: dup
IL_00f0: stloc.0
IL_00f1: stfld ""int C.<Main>d__0.<>1__state""
IL_00f6: ldarg.0
IL_00f7: ldloc.s V_6
IL_00f9: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_00fe: ldarg.0
IL_00ff: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0104: ldloca.s V_6
IL_0106: ldarg.0
IL_0107: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Main>d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Main>d__0)""
IL_010c: leave IL_0194
IL_0111: ldarg.0
IL_0112: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_0117: stloc.s V_6
IL_0119: ldarg.0
IL_011a: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_011f: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_0125: ldarg.0
IL_0126: ldc.i4.m1
IL_0127: dup
IL_0128: stloc.0
IL_0129: stfld ""int C.<Main>d__0.<>1__state""
IL_012e: ldloca.s V_6
IL_0130: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_0135: ldarg.0
IL_0136: ldfld ""object C.<Main>d__0.<>7__wrap2""
IL_013b: stloc.s V_5
IL_013d: ldloc.s V_5
IL_013f: brfalse.s IL_0158
IL_0141: ldloc.s V_5
IL_0143: isinst ""System.Exception""
IL_0148: dup
IL_0149: brtrue.s IL_014e
IL_014b: ldloc.s V_5
IL_014d: throw
IL_014e: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_0153: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_0158: ldarg.0
IL_0159: ldnull
IL_015a: stfld ""object C.<Main>d__0.<>7__wrap2""
IL_015f: ldarg.0
IL_0160: ldnull
IL_0161: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_0166: leave.s IL_0181
}
catch System.Exception
{
IL_0168: stloc.s V_8
IL_016a: ldarg.0
IL_016b: ldc.i4.s -2
IL_016d: stfld ""int C.<Main>d__0.<>1__state""
IL_0172: ldarg.0
IL_0173: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0178: ldloc.s V_8
IL_017a: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_017f: leave.s IL_0194
}
IL_0181: ldarg.0
IL_0182: ldc.i4.s -2
IL_0184: stfld ""int C.<Main>d__0.<>1__state""
IL_0189: ldarg.0
IL_018a: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_018f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_0194: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation ( System.Collections.Generic.IAsyncEnumerator<System.Int32> S.GetAsyncEnumerator([System.Threading.CancellationToken token = default(System.Threading.CancellationToken)])) (OperationKind.Invocation, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(1):
IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: token) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'await forea ... }')
IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Threading.CancellationToken, IsImplicit) (Syntax: 'await forea ... }')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IAwaitOperation (OperationKind.Await, Type: System.Boolean, IsImplicit) (Syntax: 'await forea ... }')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask<System.Boolean> System.Collections.Generic.IAsyncEnumerator<System.Int32>.MoveNextAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask<System.Boolean>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IAsyncEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> S.GetAsyncEnumerator([System.Threading.CancellationToken token = default(System.Threading.CancellationToken)])", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> S.GetAsyncEnumerator([System.Threading.CancellationToken token = default(System.Threading.CancellationToken)])", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal(1, op.Info.GetEnumeratorArguments.Length);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
public void AwaitForeach_IAsyncEnumerableT_02()
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
ref struct S : IAsyncEnumerable<int>
{
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken token = default)
{
return Get123();
}
async static IAsyncEnumerator<int> Get123()
{
await Task.Yield();
yield return 123;
}
IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(CancellationToken token) => throw null;
}
class C
{
static async Task Main()
{
await foreach (var i in new S())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Main>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()",
@"
{
// Code size 405 (0x195)
.maxstack 3
.locals init (int V_0,
S V_1,
System.Threading.CancellationToken V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter<bool> V_3,
System.Threading.Tasks.ValueTask<bool> V_4,
object V_5,
System.Runtime.CompilerServices.ValueTaskAwaiter V_6,
System.Threading.Tasks.ValueTask V_7,
System.Exception V_8)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Main>d__0.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_003c
IL_000a: ldloc.0
IL_000b: ldc.i4.1
IL_000c: beq IL_0111
IL_0011: ldarg.0
IL_0012: ldloca.s V_1
IL_0014: dup
IL_0015: initobj ""S""
IL_001b: ldloca.s V_2
IL_001d: initobj ""System.Threading.CancellationToken""
IL_0023: ldloc.2
IL_0024: call ""System.Collections.Generic.IAsyncEnumerator<int> S.GetAsyncEnumerator(System.Threading.CancellationToken)""
IL_0029: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_002e: ldarg.0
IL_002f: ldnull
IL_0030: stfld ""object C.<Main>d__0.<>7__wrap2""
IL_0035: ldarg.0
IL_0036: ldc.i4.0
IL_0037: stfld ""int C.<Main>d__0.<>7__wrap3""
IL_003c: nop
.try
{
IL_003d: ldloc.0
IL_003e: brfalse.s IL_0093
IL_0040: br.s IL_0052
IL_0042: ldarg.0
IL_0043: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_0048: callvirt ""int System.Collections.Generic.IAsyncEnumerator<int>.Current.get""
IL_004d: call ""void System.Console.Write(int)""
IL_0052: ldarg.0
IL_0053: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_0058: callvirt ""System.Threading.Tasks.ValueTask<bool> System.Collections.Generic.IAsyncEnumerator<int>.MoveNextAsync()""
IL_005d: stloc.s V_4
IL_005f: ldloca.s V_4
IL_0061: call ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> System.Threading.Tasks.ValueTask<bool>.GetAwaiter()""
IL_0066: stloc.3
IL_0067: ldloca.s V_3
IL_0069: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.IsCompleted.get""
IL_006e: brtrue.s IL_00af
IL_0070: ldarg.0
IL_0071: ldc.i4.0
IL_0072: dup
IL_0073: stloc.0
IL_0074: stfld ""int C.<Main>d__0.<>1__state""
IL_0079: ldarg.0
IL_007a: ldloc.3
IL_007b: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_0080: ldarg.0
IL_0081: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0086: ldloca.s V_3
IL_0088: ldarg.0
IL_0089: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, C.<Main>d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, ref C.<Main>d__0)""
IL_008e: leave IL_0194
IL_0093: ldarg.0
IL_0094: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_0099: stloc.3
IL_009a: ldarg.0
IL_009b: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_00a0: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool>""
IL_00a6: ldarg.0
IL_00a7: ldc.i4.m1
IL_00a8: dup
IL_00a9: stloc.0
IL_00aa: stfld ""int C.<Main>d__0.<>1__state""
IL_00af: ldloca.s V_3
IL_00b1: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.GetResult()""
IL_00b6: brtrue.s IL_0042
IL_00b8: leave.s IL_00c6
}
catch object
{
IL_00ba: stloc.s V_5
IL_00bc: ldarg.0
IL_00bd: ldloc.s V_5
IL_00bf: stfld ""object C.<Main>d__0.<>7__wrap2""
IL_00c4: leave.s IL_00c6
}
IL_00c6: ldarg.0
IL_00c7: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_00cc: brfalse.s IL_0135
IL_00ce: ldarg.0
IL_00cf: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_00d4: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_00d9: stloc.s V_7
IL_00db: ldloca.s V_7
IL_00dd: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_00e2: stloc.s V_6
IL_00e4: ldloca.s V_6
IL_00e6: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_00eb: brtrue.s IL_012e
IL_00ed: ldarg.0
IL_00ee: ldc.i4.1
IL_00ef: dup
IL_00f0: stloc.0
IL_00f1: stfld ""int C.<Main>d__0.<>1__state""
IL_00f6: ldarg.0
IL_00f7: ldloc.s V_6
IL_00f9: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_00fe: ldarg.0
IL_00ff: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0104: ldloca.s V_6
IL_0106: ldarg.0
IL_0107: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Main>d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Main>d__0)""
IL_010c: leave IL_0194
IL_0111: ldarg.0
IL_0112: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_0117: stloc.s V_6
IL_0119: ldarg.0
IL_011a: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_011f: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_0125: ldarg.0
IL_0126: ldc.i4.m1
IL_0127: dup
IL_0128: stloc.0
IL_0129: stfld ""int C.<Main>d__0.<>1__state""
IL_012e: ldloca.s V_6
IL_0130: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_0135: ldarg.0
IL_0136: ldfld ""object C.<Main>d__0.<>7__wrap2""
IL_013b: stloc.s V_5
IL_013d: ldloc.s V_5
IL_013f: brfalse.s IL_0158
IL_0141: ldloc.s V_5
IL_0143: isinst ""System.Exception""
IL_0148: dup
IL_0149: brtrue.s IL_014e
IL_014b: ldloc.s V_5
IL_014d: throw
IL_014e: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_0153: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_0158: ldarg.0
IL_0159: ldnull
IL_015a: stfld ""object C.<Main>d__0.<>7__wrap2""
IL_015f: ldarg.0
IL_0160: ldnull
IL_0161: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>7__wrap1""
IL_0166: leave.s IL_0181
}
catch System.Exception
{
IL_0168: stloc.s V_8
IL_016a: ldarg.0
IL_016b: ldc.i4.s -2
IL_016d: stfld ""int C.<Main>d__0.<>1__state""
IL_0172: ldarg.0
IL_0173: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0178: ldloc.s V_8
IL_017a: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_017f: leave.s IL_0194
}
IL_0181: ldarg.0
IL_0182: ldc.i4.s -2
IL_0184: stfld ""int C.<Main>d__0.<>1__state""
IL_0189: ldarg.0
IL_018a: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_018f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_0194: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation ( System.Collections.Generic.IAsyncEnumerator<System.Int32> S.GetAsyncEnumerator([System.Threading.CancellationToken token = default(System.Threading.CancellationToken)])) (OperationKind.Invocation, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(1):
IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: token) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'await forea ... }')
IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Threading.CancellationToken, IsImplicit) (Syntax: 'await forea ... }')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IAwaitOperation (OperationKind.Await, Type: System.Boolean, IsImplicit) (Syntax: 'await forea ... }')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask<System.Boolean> System.Collections.Generic.IAsyncEnumerator<System.Int32>.MoveNextAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask<System.Boolean>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IAsyncEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> S.GetAsyncEnumerator([System.Threading.CancellationToken token = default(System.Threading.CancellationToken)])", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> S.GetAsyncEnumerator([System.Threading.CancellationToken token = default(System.Threading.CancellationToken)])", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal(1, op.Info.GetEnumeratorArguments.Length);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
public void AwaitForeach_IAsyncEnumerableT_03()
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
ref struct S : IAsyncEnumerable<int>
{
async static IAsyncEnumerator<int> Get123()
{
await Task.Yield();
yield return 123;
}
IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(CancellationToken token) => Get123();
}
struct S2 : IAsyncEnumerable<int>
{
async static IAsyncEnumerator<int> Get456()
{
await Task.Yield();
yield return 456;
}
IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(CancellationToken token) => Get456();
}
class C
{
static async Task Main()
{
await Test1();
await Test2();
}
static async Task Test1()
{
await foreach (var i in new S())
{
System.Console.Write(i);
}
}
static async Task Test2()
{
await foreach (var i in new S2())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123456" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Test1>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()",
@"
{
// Code size 411 (0x19b)
.maxstack 3
.locals init (int V_0,
S V_1,
System.Threading.CancellationToken V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter<bool> V_3,
System.Threading.Tasks.ValueTask<bool> V_4,
object V_5,
System.Runtime.CompilerServices.ValueTaskAwaiter V_6,
System.Threading.Tasks.ValueTask V_7,
System.Exception V_8)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Test1>d__1.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0042
IL_000a: ldloc.0
IL_000b: ldc.i4.1
IL_000c: beq IL_0117
IL_0011: ldarg.0
IL_0012: ldloca.s V_1
IL_0014: dup
IL_0015: initobj ""S""
IL_001b: ldloca.s V_2
IL_001d: initobj ""System.Threading.CancellationToken""
IL_0023: ldloc.2
IL_0024: constrained. ""S""
IL_002a: callvirt ""System.Collections.Generic.IAsyncEnumerator<int> System.Collections.Generic.IAsyncEnumerable<int>.GetAsyncEnumerator(System.Threading.CancellationToken)""
IL_002f: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test1>d__1.<>7__wrap1""
IL_0034: ldarg.0
IL_0035: ldnull
IL_0036: stfld ""object C.<Test1>d__1.<>7__wrap2""
IL_003b: ldarg.0
IL_003c: ldc.i4.0
IL_003d: stfld ""int C.<Test1>d__1.<>7__wrap3""
IL_0042: nop
.try
{
IL_0043: ldloc.0
IL_0044: brfalse.s IL_0099
IL_0046: br.s IL_0058
IL_0048: ldarg.0
IL_0049: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test1>d__1.<>7__wrap1""
IL_004e: callvirt ""int System.Collections.Generic.IAsyncEnumerator<int>.Current.get""
IL_0053: call ""void System.Console.Write(int)""
IL_0058: ldarg.0
IL_0059: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test1>d__1.<>7__wrap1""
IL_005e: callvirt ""System.Threading.Tasks.ValueTask<bool> System.Collections.Generic.IAsyncEnumerator<int>.MoveNextAsync()""
IL_0063: stloc.s V_4
IL_0065: ldloca.s V_4
IL_0067: call ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> System.Threading.Tasks.ValueTask<bool>.GetAwaiter()""
IL_006c: stloc.3
IL_006d: ldloca.s V_3
IL_006f: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.IsCompleted.get""
IL_0074: brtrue.s IL_00b5
IL_0076: ldarg.0
IL_0077: ldc.i4.0
IL_0078: dup
IL_0079: stloc.0
IL_007a: stfld ""int C.<Test1>d__1.<>1__state""
IL_007f: ldarg.0
IL_0080: ldloc.3
IL_0081: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test1>d__1.<>u__1""
IL_0086: ldarg.0
IL_0087: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test1>d__1.<>t__builder""
IL_008c: ldloca.s V_3
IL_008e: ldarg.0
IL_008f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, C.<Test1>d__1>(ref System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, ref C.<Test1>d__1)""
IL_0094: leave IL_019a
IL_0099: ldarg.0
IL_009a: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test1>d__1.<>u__1""
IL_009f: stloc.3
IL_00a0: ldarg.0
IL_00a1: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test1>d__1.<>u__1""
IL_00a6: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool>""
IL_00ac: ldarg.0
IL_00ad: ldc.i4.m1
IL_00ae: dup
IL_00af: stloc.0
IL_00b0: stfld ""int C.<Test1>d__1.<>1__state""
IL_00b5: ldloca.s V_3
IL_00b7: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.GetResult()""
IL_00bc: brtrue.s IL_0048
IL_00be: leave.s IL_00cc
}
catch object
{
IL_00c0: stloc.s V_5
IL_00c2: ldarg.0
IL_00c3: ldloc.s V_5
IL_00c5: stfld ""object C.<Test1>d__1.<>7__wrap2""
IL_00ca: leave.s IL_00cc
}
IL_00cc: ldarg.0
IL_00cd: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test1>d__1.<>7__wrap1""
IL_00d2: brfalse.s IL_013b
IL_00d4: ldarg.0
IL_00d5: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test1>d__1.<>7__wrap1""
IL_00da: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_00df: stloc.s V_7
IL_00e1: ldloca.s V_7
IL_00e3: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_00e8: stloc.s V_6
IL_00ea: ldloca.s V_6
IL_00ec: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_00f1: brtrue.s IL_0134
IL_00f3: ldarg.0
IL_00f4: ldc.i4.1
IL_00f5: dup
IL_00f6: stloc.0
IL_00f7: stfld ""int C.<Test1>d__1.<>1__state""
IL_00fc: ldarg.0
IL_00fd: ldloc.s V_6
IL_00ff: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test1>d__1.<>u__2""
IL_0104: ldarg.0
IL_0105: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test1>d__1.<>t__builder""
IL_010a: ldloca.s V_6
IL_010c: ldarg.0
IL_010d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Test1>d__1>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Test1>d__1)""
IL_0112: leave IL_019a
IL_0117: ldarg.0
IL_0118: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test1>d__1.<>u__2""
IL_011d: stloc.s V_6
IL_011f: ldarg.0
IL_0120: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test1>d__1.<>u__2""
IL_0125: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_012b: ldarg.0
IL_012c: ldc.i4.m1
IL_012d: dup
IL_012e: stloc.0
IL_012f: stfld ""int C.<Test1>d__1.<>1__state""
IL_0134: ldloca.s V_6
IL_0136: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_013b: ldarg.0
IL_013c: ldfld ""object C.<Test1>d__1.<>7__wrap2""
IL_0141: stloc.s V_5
IL_0143: ldloc.s V_5
IL_0145: brfalse.s IL_015e
IL_0147: ldloc.s V_5
IL_0149: isinst ""System.Exception""
IL_014e: dup
IL_014f: brtrue.s IL_0154
IL_0151: ldloc.s V_5
IL_0153: throw
IL_0154: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_0159: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_015e: ldarg.0
IL_015f: ldnull
IL_0160: stfld ""object C.<Test1>d__1.<>7__wrap2""
IL_0165: ldarg.0
IL_0166: ldnull
IL_0167: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test1>d__1.<>7__wrap1""
IL_016c: leave.s IL_0187
}
catch System.Exception
{
IL_016e: stloc.s V_8
IL_0170: ldarg.0
IL_0171: ldc.i4.s -2
IL_0173: stfld ""int C.<Test1>d__1.<>1__state""
IL_0178: ldarg.0
IL_0179: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test1>d__1.<>t__builder""
IL_017e: ldloc.s V_8
IL_0180: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_0185: leave.s IL_019a
}
IL_0187: ldarg.0
IL_0188: ldc.i4.s -2
IL_018a: stfld ""int C.<Test1>d__1.<>1__state""
IL_018f: ldarg.0
IL_0190: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test1>d__1.<>t__builder""
IL_0195: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_019a: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test1").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation (virtual System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])) (OperationKind.Invocation, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(1):
IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: cancellationToken) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'new S()')
IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Threading.CancellationToken, IsImplicit) (Syntax: 'new S()')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IAwaitOperation (OperationKind.Await, Type: System.Boolean, IsImplicit) (Syntax: 'await forea ... }')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask<System.Boolean> System.Collections.Generic.IAsyncEnumerator<System.Int32>.MoveNextAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask<System.Boolean>, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IAsyncEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IAsyncDisposable, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ImplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().First();
AssertEx.Equal("new S()", foreachSyntax.Expression.ToString());
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal(1, op.Info.GetEnumeratorArguments.Length);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerableT_04(bool addStructConstraint)
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
ref struct S : IAsyncEnumerable<int>
{
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken token = default)
{
return Get123();
}
async static IAsyncEnumerator<int> Get123()
{
await Task.Yield();
yield return 123;
}
}
class C
{
static async Task Main()
{
await Test<S>();
}
static async Task Test<T>() where T : " + (addStructConstraint ? "struct, " : "") + @"IAsyncEnumerable<int>, allows ref struct
{
await foreach (var i in default(T))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Test>d__1<T>.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()",
@"
{
// Code size 411 (0x19b)
.maxstack 3
.locals init (int V_0,
T V_1,
System.Threading.CancellationToken V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter<bool> V_3,
System.Threading.Tasks.ValueTask<bool> V_4,
object V_5,
System.Runtime.CompilerServices.ValueTaskAwaiter V_6,
System.Threading.Tasks.ValueTask V_7,
System.Exception V_8)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Test>d__1<T>.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0042
IL_000a: ldloc.0
IL_000b: ldc.i4.1
IL_000c: beq IL_0117
IL_0011: ldarg.0
IL_0012: ldloca.s V_1
IL_0014: dup
IL_0015: initobj ""T""
IL_001b: ldloca.s V_2
IL_001d: initobj ""System.Threading.CancellationToken""
IL_0023: ldloc.2
IL_0024: constrained. ""T""
IL_002a: callvirt ""System.Collections.Generic.IAsyncEnumerator<int> System.Collections.Generic.IAsyncEnumerable<int>.GetAsyncEnumerator(System.Threading.CancellationToken)""
IL_002f: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_0034: ldarg.0
IL_0035: ldnull
IL_0036: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_003b: ldarg.0
IL_003c: ldc.i4.0
IL_003d: stfld ""int C.<Test>d__1<T>.<>7__wrap3""
IL_0042: nop
.try
{
IL_0043: ldloc.0
IL_0044: brfalse.s IL_0099
IL_0046: br.s IL_0058
IL_0048: ldarg.0
IL_0049: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_004e: callvirt ""int System.Collections.Generic.IAsyncEnumerator<int>.Current.get""
IL_0053: call ""void System.Console.Write(int)""
IL_0058: ldarg.0
IL_0059: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_005e: callvirt ""System.Threading.Tasks.ValueTask<bool> System.Collections.Generic.IAsyncEnumerator<int>.MoveNextAsync()""
IL_0063: stloc.s V_4
IL_0065: ldloca.s V_4
IL_0067: call ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> System.Threading.Tasks.ValueTask<bool>.GetAwaiter()""
IL_006c: stloc.3
IL_006d: ldloca.s V_3
IL_006f: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.IsCompleted.get""
IL_0074: brtrue.s IL_00b5
IL_0076: ldarg.0
IL_0077: ldc.i4.0
IL_0078: dup
IL_0079: stloc.0
IL_007a: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_007f: ldarg.0
IL_0080: ldloc.3
IL_0081: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_0086: ldarg.0
IL_0087: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_008c: ldloca.s V_3
IL_008e: ldarg.0
IL_008f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, ref C.<Test>d__1<T>)""
IL_0094: leave IL_019a
IL_0099: ldarg.0
IL_009a: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_009f: stloc.3
IL_00a0: ldarg.0
IL_00a1: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_00a6: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool>""
IL_00ac: ldarg.0
IL_00ad: ldc.i4.m1
IL_00ae: dup
IL_00af: stloc.0
IL_00b0: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00b5: ldloca.s V_3
IL_00b7: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.GetResult()""
IL_00bc: brtrue.s IL_0048
IL_00be: leave.s IL_00cc
}
catch object
{
IL_00c0: stloc.s V_5
IL_00c2: ldarg.0
IL_00c3: ldloc.s V_5
IL_00c5: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_00ca: leave.s IL_00cc
}
IL_00cc: ldarg.0
IL_00cd: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_00d2: brfalse.s IL_013b
IL_00d4: ldarg.0
IL_00d5: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_00da: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_00df: stloc.s V_7
IL_00e1: ldloca.s V_7
IL_00e3: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_00e8: stloc.s V_6
IL_00ea: ldloca.s V_6
IL_00ec: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_00f1: brtrue.s IL_0134
IL_00f3: ldarg.0
IL_00f4: ldc.i4.1
IL_00f5: dup
IL_00f6: stloc.0
IL_00f7: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00fc: ldarg.0
IL_00fd: ldloc.s V_6
IL_00ff: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_0104: ldarg.0
IL_0105: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_010a: ldloca.s V_6
IL_010c: ldarg.0
IL_010d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Test>d__1<T>)""
IL_0112: leave IL_019a
IL_0117: ldarg.0
IL_0118: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_011d: stloc.s V_6
IL_011f: ldarg.0
IL_0120: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_0125: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_012b: ldarg.0
IL_012c: ldc.i4.m1
IL_012d: dup
IL_012e: stloc.0
IL_012f: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0134: ldloca.s V_6
IL_0136: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_013b: ldarg.0
IL_013c: ldfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_0141: stloc.s V_5
IL_0143: ldloc.s V_5
IL_0145: brfalse.s IL_015e
IL_0147: ldloc.s V_5
IL_0149: isinst ""System.Exception""
IL_014e: dup
IL_014f: brtrue.s IL_0154
IL_0151: ldloc.s V_5
IL_0153: throw
IL_0154: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_0159: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_015e: ldarg.0
IL_015f: ldnull
IL_0160: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_0165: ldarg.0
IL_0166: ldnull
IL_0167: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_016c: leave.s IL_0187
}
catch System.Exception
{
IL_016e: stloc.s V_8
IL_0170: ldarg.0
IL_0171: ldc.i4.s -2
IL_0173: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0178: ldarg.0
IL_0179: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_017e: ldloc.s V_8
IL_0180: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_0185: leave.s IL_019a
}
IL_0187: ldarg.0
IL_0188: ldc.i4.s -2
IL_018a: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_018f: ldarg.0
IL_0190: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_0195: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_019a: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'default(T)')
Value:
IInvocationOperation (virtual System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])) (OperationKind.Invocation, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 'default(T)')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IDefaultValueOperation (OperationKind.DefaultValue, Type: T) (Syntax: 'default(T)')
Arguments(1):
IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: cancellationToken) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'await forea ... }')
IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Threading.CancellationToken, IsImplicit) (Syntax: 'await forea ... }')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IAwaitOperation (OperationKind.Await, Type: System.Boolean, IsImplicit) (Syntax: 'await forea ... }')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask<System.Boolean> System.Collections.Generic.IAsyncEnumerator<System.Int32>.MoveNextAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask<System.Boolean>, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IAsyncEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'default(T)')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'default(T)')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal(1, op.Info.GetEnumeratorArguments.Length);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerableT_05(bool addStructConstraint)
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
interface IMyAsyncEnumerable<T>
{
IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
ref struct S : IMyAsyncEnumerable<int>
{
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken token = default)
{
return Get123();
}
async static IAsyncEnumerator<int> Get123()
{
await Task.Yield();
yield return 123;
}
}
class C
{
static async Task Main()
{
await Test<S>();
}
static async Task Test<T>() where T : " + (addStructConstraint ? "struct, " : "") + @"IMyAsyncEnumerable<int>, allows ref struct
{
await foreach (var i in default(T))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.<Test>d__1<T>.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()",
@"
{
// Code size 411 (0x19b)
.maxstack 3
.locals init (int V_0,
T V_1,
System.Threading.CancellationToken V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter<bool> V_3,
System.Threading.Tasks.ValueTask<bool> V_4,
object V_5,
System.Runtime.CompilerServices.ValueTaskAwaiter V_6,
System.Threading.Tasks.ValueTask V_7,
System.Exception V_8)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Test>d__1<T>.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0042
IL_000a: ldloc.0
IL_000b: ldc.i4.1
IL_000c: beq IL_0117
IL_0011: ldarg.0
IL_0012: ldloca.s V_1
IL_0014: dup
IL_0015: initobj ""T""
IL_001b: ldloca.s V_2
IL_001d: initobj ""System.Threading.CancellationToken""
IL_0023: ldloc.2
IL_0024: constrained. ""T""
IL_002a: callvirt ""System.Collections.Generic.IAsyncEnumerator<int> IMyAsyncEnumerable<int>.GetAsyncEnumerator(System.Threading.CancellationToken)""
IL_002f: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_0034: ldarg.0
IL_0035: ldnull
IL_0036: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_003b: ldarg.0
IL_003c: ldc.i4.0
IL_003d: stfld ""int C.<Test>d__1<T>.<>7__wrap3""
IL_0042: nop
.try
{
IL_0043: ldloc.0
IL_0044: brfalse.s IL_0099
IL_0046: br.s IL_0058
IL_0048: ldarg.0
IL_0049: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_004e: callvirt ""int System.Collections.Generic.IAsyncEnumerator<int>.Current.get""
IL_0053: call ""void System.Console.Write(int)""
IL_0058: ldarg.0
IL_0059: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_005e: callvirt ""System.Threading.Tasks.ValueTask<bool> System.Collections.Generic.IAsyncEnumerator<int>.MoveNextAsync()""
IL_0063: stloc.s V_4
IL_0065: ldloca.s V_4
IL_0067: call ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> System.Threading.Tasks.ValueTask<bool>.GetAwaiter()""
IL_006c: stloc.3
IL_006d: ldloca.s V_3
IL_006f: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.IsCompleted.get""
IL_0074: brtrue.s IL_00b5
IL_0076: ldarg.0
IL_0077: ldc.i4.0
IL_0078: dup
IL_0079: stloc.0
IL_007a: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_007f: ldarg.0
IL_0080: ldloc.3
IL_0081: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_0086: ldarg.0
IL_0087: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_008c: ldloca.s V_3
IL_008e: ldarg.0
IL_008f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, ref C.<Test>d__1<T>)""
IL_0094: leave IL_019a
IL_0099: ldarg.0
IL_009a: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_009f: stloc.3
IL_00a0: ldarg.0
IL_00a1: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_00a6: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool>""
IL_00ac: ldarg.0
IL_00ad: ldc.i4.m1
IL_00ae: dup
IL_00af: stloc.0
IL_00b0: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00b5: ldloca.s V_3
IL_00b7: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.GetResult()""
IL_00bc: brtrue.s IL_0048
IL_00be: leave.s IL_00cc
}
catch object
{
IL_00c0: stloc.s V_5
IL_00c2: ldarg.0
IL_00c3: ldloc.s V_5
IL_00c5: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_00ca: leave.s IL_00cc
}
IL_00cc: ldarg.0
IL_00cd: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_00d2: brfalse.s IL_013b
IL_00d4: ldarg.0
IL_00d5: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_00da: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_00df: stloc.s V_7
IL_00e1: ldloca.s V_7
IL_00e3: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_00e8: stloc.s V_6
IL_00ea: ldloca.s V_6
IL_00ec: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_00f1: brtrue.s IL_0134
IL_00f3: ldarg.0
IL_00f4: ldc.i4.1
IL_00f5: dup
IL_00f6: stloc.0
IL_00f7: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00fc: ldarg.0
IL_00fd: ldloc.s V_6
IL_00ff: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_0104: ldarg.0
IL_0105: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_010a: ldloca.s V_6
IL_010c: ldarg.0
IL_010d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Test>d__1<T>)""
IL_0112: leave IL_019a
IL_0117: ldarg.0
IL_0118: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_011d: stloc.s V_6
IL_011f: ldarg.0
IL_0120: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_0125: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_012b: ldarg.0
IL_012c: ldc.i4.m1
IL_012d: dup
IL_012e: stloc.0
IL_012f: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0134: ldloca.s V_6
IL_0136: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_013b: ldarg.0
IL_013c: ldfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_0141: stloc.s V_5
IL_0143: ldloc.s V_5
IL_0145: brfalse.s IL_015e
IL_0147: ldloc.s V_5
IL_0149: isinst ""System.Exception""
IL_014e: dup
IL_014f: brtrue.s IL_0154
IL_0151: ldloc.s V_5
IL_0153: throw
IL_0154: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_0159: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_015e: ldarg.0
IL_015f: ldnull
IL_0160: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_0165: ldarg.0
IL_0166: ldnull
IL_0167: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_016c: leave.s IL_0187
}
catch System.Exception
{
IL_016e: stloc.s V_8
IL_0170: ldarg.0
IL_0171: ldc.i4.s -2
IL_0173: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0178: ldarg.0
IL_0179: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_017e: ldloc.s V_8
IL_0180: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_0185: leave.s IL_019a
}
IL_0187: ldarg.0
IL_0188: ldc.i4.s -2
IL_018a: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_018f: ldarg.0
IL_0190: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_0195: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_019a: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'default(T)')
Value:
IInvocationOperation (virtual System.Collections.Generic.IAsyncEnumerator<System.Int32> IMyAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])) (OperationKind.Invocation, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 'default(T)')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IDefaultValueOperation (OperationKind.DefaultValue, Type: T) (Syntax: 'default(T)')
Arguments(1):
IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: cancellationToken) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'await forea ... }')
IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Threading.CancellationToken, IsImplicit) (Syntax: 'await forea ... }')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IAwaitOperation (OperationKind.Await, Type: System.Boolean, IsImplicit) (Syntax: 'await forea ... }')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask<System.Boolean> System.Collections.Generic.IAsyncEnumerator<System.Int32>.MoveNextAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask<System.Boolean>, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IAsyncEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'default(T)')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'default(T)')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> IMyAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> IMyAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal(1, op.Info.GetEnumeratorArguments.Length);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerableT_06(bool addStructConstraint)
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
interface IMyAsyncEnumerable1<T>
{
IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
interface IMyAsyncEnumerable2<T>
{
IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
ref struct S : IMyAsyncEnumerable1<int>, IMyAsyncEnumerable2<int>
{
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken token = default)
{
return Get123();
}
async static IAsyncEnumerator<int> Get123()
{
await Task.Yield();
yield return 123;
}
}
class C
{
static async Task Main()
{
await Test<S>();
}
static async Task Test<T>() where T : " + (addStructConstraint ? "struct, " : "") + @"IMyAsyncEnumerable1<int>, IMyAsyncEnumerable2<int>, allows ref struct
{
await foreach (var i in default(T))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (39,33): warning CS0278: 'T' does not implement the 'collection' pattern. 'IMyAsyncEnumerable1<int>.GetAsyncEnumerator(CancellationToken)' is ambiguous with 'IMyAsyncEnumerable2<int>.GetAsyncEnumerator(CancellationToken)'.
// await foreach (var i in default(T))
Diagnostic(ErrorCode.WRN_PatternIsAmbiguous, "default(T)").WithArguments("T", "collection", "IMyAsyncEnumerable1<int>.GetAsyncEnumerator(System.Threading.CancellationToken)", "IMyAsyncEnumerable2<int>.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(39, 33),
// (39,33): error CS8411: Asynchronous foreach statement cannot operate on variables of type 'T' because 'T' does not contain a suitable public instance or extension definition for 'GetAsyncEnumerator'
// await foreach (var i in default(T))
Diagnostic(ErrorCode.ERR_AwaitForEachMissingMember, "default(T)").WithArguments("T", "GetAsyncEnumerator").WithLocation(39, 33)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
Assert.Null(info.GetEnumeratorMethod);
Assert.Null(info.ElementType);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.Null(op.Info);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerableT_07(bool addStructConstraint)
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
interface IMyAsyncEnumerable1<T>
{
IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
interface IMyAsyncEnumerable2<T>
{
IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
ref struct S : IMyAsyncEnumerable1<int>, IMyAsyncEnumerable2<int>, IAsyncEnumerable<int>
{
IAsyncEnumerator<int> IMyAsyncEnumerable1<int>.GetAsyncEnumerator(CancellationToken token) => throw null;
IAsyncEnumerator<int> IMyAsyncEnumerable2<int>.GetAsyncEnumerator(CancellationToken token) => throw null;
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken token = default)
{
return Get123();
}
async static IAsyncEnumerator<int> Get123()
{
await Task.Yield();
yield return 123;
}
}
class C
{
static async Task Main()
{
await Test<S>();
}
static async Task Test<T>() where T : " + (addStructConstraint ? "struct, " : "") + @"IMyAsyncEnumerable1<int>, IMyAsyncEnumerable2<int>, IAsyncEnumerable<int>, allows ref struct
{
await foreach (var i in default(T))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(
// (42,33): warning CS0278: 'T' does not implement the 'collection' pattern. 'IMyAsyncEnumerable1<int>.GetAsyncEnumerator(CancellationToken)' is ambiguous with 'IMyAsyncEnumerable2<int>.GetAsyncEnumerator(CancellationToken)'.
// await foreach (var i in default(T))
Diagnostic(ErrorCode.WRN_PatternIsAmbiguous, "default(T)").WithArguments("T", "collection", "IMyAsyncEnumerable1<int>.GetAsyncEnumerator(System.Threading.CancellationToken)", "IMyAsyncEnumerable2<int>.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(42, 33)
);
verifier.VerifyIL("C.<Test>d__1<T>.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()",
@"
{
// Code size 411 (0x19b)
.maxstack 3
.locals init (int V_0,
T V_1,
System.Threading.CancellationToken V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter<bool> V_3,
System.Threading.Tasks.ValueTask<bool> V_4,
object V_5,
System.Runtime.CompilerServices.ValueTaskAwaiter V_6,
System.Threading.Tasks.ValueTask V_7,
System.Exception V_8)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Test>d__1<T>.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0042
IL_000a: ldloc.0
IL_000b: ldc.i4.1
IL_000c: beq IL_0117
IL_0011: ldarg.0
IL_0012: ldloca.s V_1
IL_0014: dup
IL_0015: initobj ""T""
IL_001b: ldloca.s V_2
IL_001d: initobj ""System.Threading.CancellationToken""
IL_0023: ldloc.2
IL_0024: constrained. ""T""
IL_002a: callvirt ""System.Collections.Generic.IAsyncEnumerator<int> System.Collections.Generic.IAsyncEnumerable<int>.GetAsyncEnumerator(System.Threading.CancellationToken)""
IL_002f: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_0034: ldarg.0
IL_0035: ldnull
IL_0036: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_003b: ldarg.0
IL_003c: ldc.i4.0
IL_003d: stfld ""int C.<Test>d__1<T>.<>7__wrap3""
IL_0042: nop
.try
{
IL_0043: ldloc.0
IL_0044: brfalse.s IL_0099
IL_0046: br.s IL_0058
IL_0048: ldarg.0
IL_0049: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_004e: callvirt ""int System.Collections.Generic.IAsyncEnumerator<int>.Current.get""
IL_0053: call ""void System.Console.Write(int)""
IL_0058: ldarg.0
IL_0059: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_005e: callvirt ""System.Threading.Tasks.ValueTask<bool> System.Collections.Generic.IAsyncEnumerator<int>.MoveNextAsync()""
IL_0063: stloc.s V_4
IL_0065: ldloca.s V_4
IL_0067: call ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> System.Threading.Tasks.ValueTask<bool>.GetAwaiter()""
IL_006c: stloc.3
IL_006d: ldloca.s V_3
IL_006f: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.IsCompleted.get""
IL_0074: brtrue.s IL_00b5
IL_0076: ldarg.0
IL_0077: ldc.i4.0
IL_0078: dup
IL_0079: stloc.0
IL_007a: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_007f: ldarg.0
IL_0080: ldloc.3
IL_0081: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_0086: ldarg.0
IL_0087: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_008c: ldloca.s V_3
IL_008e: ldarg.0
IL_008f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, ref C.<Test>d__1<T>)""
IL_0094: leave IL_019a
IL_0099: ldarg.0
IL_009a: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_009f: stloc.3
IL_00a0: ldarg.0
IL_00a1: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Test>d__1<T>.<>u__1""
IL_00a6: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool>""
IL_00ac: ldarg.0
IL_00ad: ldc.i4.m1
IL_00ae: dup
IL_00af: stloc.0
IL_00b0: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00b5: ldloca.s V_3
IL_00b7: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.GetResult()""
IL_00bc: brtrue.s IL_0048
IL_00be: leave.s IL_00cc
}
catch object
{
IL_00c0: stloc.s V_5
IL_00c2: ldarg.0
IL_00c3: ldloc.s V_5
IL_00c5: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_00ca: leave.s IL_00cc
}
IL_00cc: ldarg.0
IL_00cd: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_00d2: brfalse.s IL_013b
IL_00d4: ldarg.0
IL_00d5: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_00da: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_00df: stloc.s V_7
IL_00e1: ldloca.s V_7
IL_00e3: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_00e8: stloc.s V_6
IL_00ea: ldloca.s V_6
IL_00ec: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_00f1: brtrue.s IL_0134
IL_00f3: ldarg.0
IL_00f4: ldc.i4.1
IL_00f5: dup
IL_00f6: stloc.0
IL_00f7: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_00fc: ldarg.0
IL_00fd: ldloc.s V_6
IL_00ff: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_0104: ldarg.0
IL_0105: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_010a: ldloca.s V_6
IL_010c: ldarg.0
IL_010d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Test>d__1<T>>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Test>d__1<T>)""
IL_0112: leave IL_019a
IL_0117: ldarg.0
IL_0118: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_011d: stloc.s V_6
IL_011f: ldarg.0
IL_0120: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Test>d__1<T>.<>u__2""
IL_0125: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_012b: ldarg.0
IL_012c: ldc.i4.m1
IL_012d: dup
IL_012e: stloc.0
IL_012f: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0134: ldloca.s V_6
IL_0136: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_013b: ldarg.0
IL_013c: ldfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_0141: stloc.s V_5
IL_0143: ldloc.s V_5
IL_0145: brfalse.s IL_015e
IL_0147: ldloc.s V_5
IL_0149: isinst ""System.Exception""
IL_014e: dup
IL_014f: brtrue.s IL_0154
IL_0151: ldloc.s V_5
IL_0153: throw
IL_0154: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_0159: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_015e: ldarg.0
IL_015f: ldnull
IL_0160: stfld ""object C.<Test>d__1<T>.<>7__wrap2""
IL_0165: ldarg.0
IL_0166: ldnull
IL_0167: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Test>d__1<T>.<>7__wrap1""
IL_016c: leave.s IL_0187
}
catch System.Exception
{
IL_016e: stloc.s V_8
IL_0170: ldarg.0
IL_0171: ldc.i4.s -2
IL_0173: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_0178: ldarg.0
IL_0179: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_017e: ldloc.s V_8
IL_0180: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_0185: leave.s IL_019a
}
IL_0187: ldarg.0
IL_0188: ldc.i4.s -2
IL_018a: stfld ""int C.<Test>d__1<T>.<>1__state""
IL_018f: ldarg.0
IL_0190: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Test>d__1<T>.<>t__builder""
IL_0195: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_019a: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'default(T)')
Value:
IInvocationOperation (virtual System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])) (OperationKind.Invocation, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 'default(T)')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IDefaultValueOperation (OperationKind.DefaultValue, Type: T) (Syntax: 'default(T)')
Arguments(1):
IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: cancellationToken) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'default(T)')
IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Threading.CancellationToken, IsImplicit) (Syntax: 'default(T)')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IAwaitOperation (OperationKind.Await, Type: System.Boolean, IsImplicit) (Syntax: 'await forea ... }')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask<System.Boolean> System.Collections.Generic.IAsyncEnumerator<System.Int32>.MoveNextAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask<System.Boolean>, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Int32 i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Int32 System.Collections.Generic.IAsyncEnumerator<System.Int32>.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
Block[B4] - Block
Predecessors (0)
Statements (0)
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'default(T)')
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'default(T)')
Expression:
IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'default(T)')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IAsyncDisposable, IsImplicit) (Syntax: 'default(T)')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ImplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IAsyncEnumerator<System.Int32>, IsImplicit) (Syntax: 'default(T)')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Int32", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.Generic.IAsyncEnumerator<System.Int32> System.Collections.Generic.IAsyncEnumerable<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)])", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal(1, op.Info.GetEnumeratorArguments.Length);
AssertEx.Equal("System.Int32", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/73563")]
public void AwaitForeach_IAsyncEnumerableT_LanguageVersion_01()
{
var src1 = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public ref struct S : IAsyncEnumerable<int>
{
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken token = default) => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
using System.Threading.Tasks;
class C
{
static async Task Main()
{
await foreach (var i in new S())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitForeach_IAsyncEnumerableT_LanguageVersion_03()
{
var src1 = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public ref struct S : IAsyncEnumerable<int>
{
IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(CancellationToken token) => throw null;
}
public struct S2 : IAsyncEnumerable<int>
{
IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(CancellationToken token) => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static async System.Threading.Tasks.Task Main()
{
await foreach (var i in new S())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
// Even though semantic analysis didn't produce any errors in C# 12 compiler, an attempt to emit was failing with
// "Unable to determine specific cause of the failure" error.
comp2.VerifyEmitDiagnostics(
// (6,33): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// await foreach (var i in new S())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new S()").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 33)
);
comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (6,23): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// public ref struct S : IAsyncEnumerable<int>
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "IAsyncEnumerable<int>").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 23)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
var src3 = @"
class C
{
static async System.Threading.Tasks.Task Main()
{
await foreach (var i in new S2())
{
}
}
}
";
var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp3.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitForeach_IAsyncEnumerableT_LanguageVersion_04()
{
var src = @"
using System.Collections.Generic;
using System.Threading.Tasks;
class C
{
static async Task Test<T>() where T : IAsyncEnumerable<int>, allows ref struct
{
await foreach (var i in default(T))
{
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp.VerifyEmitDiagnostics(
// (7,73): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static async Task Test<T>() where T : IAsyncEnumerable<int>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(7, 73)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics();
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitForeach_IAsyncEnumerableT_LanguageVersion_05()
{
var src1 = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public interface IMyAsyncEnumerable<T>
{
IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
using System.Threading.Tasks;
class C
{
static async Task Test<T>() where T : IMyAsyncEnumerable<int>, allows ref struct
{
await foreach (var i in default(T))
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (6,75): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static async Task Test<T>() where T : IMyAsyncEnumerable<int>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(6, 75)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitForeach_Pattern()
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
ref struct S
{
public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken token = default)
{
return Get123();
}
async static IAsyncEnumerator<int> Get123()
{
await Task.Yield();
yield return 123;
}
}
class C
{
static async Task Main()
{
await foreach (var i in new S())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerator_01(bool s1IsRefStruct)
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IAsyncEnumerator<int>
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (27,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in new S1())
{
System.Console.Write(i);
}").WithArguments("S2").WithLocation(27, 9)
);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerator_03(bool s1IsRefStruct)
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IAsyncEnumerator<int>
{
public int Current => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
int IAsyncEnumerator<int>.Current => throw null;
ValueTask System.IAsyncDisposable.DisposeAsync() => throw null;
ValueTask<bool> IAsyncEnumerator<int>.MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (30,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in new S1())
{
System.Console.Write(i);
}").WithArguments("S2").WithLocation(30, 9)
);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerator_05(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
interface IGetEnumerator<TEnumerator> where TEnumerator : IAsyncEnumerator<int>, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IAsyncEnumerator<int>
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"IAsyncEnumerator<int>, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (39,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}").WithArguments("TEnumerator").WithLocation(39, 9)
);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerator_07(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System.Threading;
using System.Threading.Tasks;
interface IMyAsyncEnumerator<T>
{
T Current {get;}
ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : IMyAsyncEnumerator<int>, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IMyAsyncEnumerator<int>
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"IMyAsyncEnumerator<int>, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (45,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}").WithArguments("TEnumerator").WithLocation(45, 9)
);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerator_08(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System.Threading;
using System.Threading.Tasks;
interface IMyAsyncEnumerator1<T>
{
T Current {get;}
ValueTask<bool> MoveNextAsync();
}
interface IMyAsyncEnumerator2<T>
{
T Current {get;}
ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : IMyAsyncEnumerator1<int>, IMyAsyncEnumerator2<int>, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IMyAsyncEnumerator1<int>, IMyAsyncEnumerator2<int>
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"IMyAsyncEnumerator1<int>, IMyAsyncEnumerator2<int>, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (52,33): error CS8412: Asynchronous foreach requires that the return type 'TEnumerator' of 'IGetEnumerator<TEnumerator>.GetAsyncEnumerator(CancellationToken)' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "default(TEnumerable)").WithArguments("TEnumerator", "IGetEnumerator<TEnumerator>.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(52, 33)
);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncEnumerator_09(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
interface IMyAsyncEnumerator1<T>
{
T Current {get;}
ValueTask<bool> MoveNextAsync();
}
interface IMyAsyncEnumerator2<T>
{
T Current {get;}
ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : IMyAsyncEnumerator1<int>, IMyAsyncEnumerator2<int>, IAsyncEnumerator<int>, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IMyAsyncEnumerator1<int>, IMyAsyncEnumerator2<int>, IAsyncEnumerator<int>
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"IMyAsyncEnumerator1<int>, IMyAsyncEnumerator2<int>, IAsyncEnumerator<int>, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (53,33): error CS8412: Asynchronous foreach requires that the return type 'TEnumerator' of 'IGetEnumerator<TEnumerator>.GetAsyncEnumerator(CancellationToken)' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "default(TEnumerable)").WithArguments("TEnumerator", "IGetEnumerator<TEnumerator>.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(53, 33)
);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncDisposable_01(bool s1IsRefStruct)
{
var src = @"
using System;
using System.Threading;
using System.Threading.Tasks;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IAsyncDisposable
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (27,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in new S1())
{
System.Console.Write(i);
}").WithArguments("S2").WithLocation(27, 9)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Threading.Tasks.ValueTask S2.DisposeAsync()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
AssertEx.Equal("System.Threading.Tasks.ValueTask S2.DisposeAsync()", op.Info.PatternDisposeMethod.ToTestDisplayString());
Assert.True(op.Info.DisposeArguments.IsEmpty);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncDisposable_02(bool s1IsRefStruct)
{
var src = @"
using System;
using System.Threading;
using System.Threading.Tasks;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IAsyncDisposable
{
public int Current => throw null;
ValueTask IAsyncDisposable.DisposeAsync() => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (27,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in new S1())
{
System.Console.Write(i);
}").WithArguments("S2").WithLocation(27, 9)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Threading.Tasks.ValueTask S2.DisposeAsync()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
AssertEx.Equal("System.Threading.Tasks.ValueTask S2.DisposeAsync()", op.Info.PatternDisposeMethod.ToTestDisplayString());
Assert.True(op.Info.DisposeArguments.IsEmpty);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncDisposable_03(bool s1IsRefStruct)
{
var src = @"
using System;
using System.Threading;
using System.Threading.Tasks;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : IAsyncDisposable
{
public int Current => throw null;
ValueTask IAsyncDisposable.DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (27,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in new S1())
{
System.Console.Write(i);
}").WithArguments("S2").WithLocation(27, 9)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncDisposable_04(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System;
using System.Threading;
using System.Threading.Tasks;
interface ICustomEnumerator
{
public int Current {get;}
public ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : ICustomEnumerator, IAsyncDisposable
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"ICustomEnumerator, IAsyncDisposable, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (46,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}").WithArguments("TEnumerator").WithLocation(46, 9)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
AssertEx.Equal("System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()", op.Info.PatternDisposeMethod.ToTestDisplayString());
Assert.True(op.Info.DisposeArguments.IsEmpty);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncDisposable_06(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System.Threading;
using System.Threading.Tasks;
interface ICustomEnumerator
{
public int Current {get;}
public ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
interface IMyAsyncDisposable
{
ValueTask DisposeAsync();
}
ref struct S2 : ICustomEnumerator, IMyAsyncDisposable
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"ICustomEnumerator, IMyAsyncDisposable, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (50,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}").WithArguments("TEnumerator").WithLocation(50, 9)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Threading.Tasks.ValueTask IMyAsyncDisposable.DisposeAsync()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
Assert.True(op.Info.NeedsDispose);
Assert.False(op.Info.KnownToImplementIDisposable);
AssertEx.Equal("System.Threading.Tasks.ValueTask IMyAsyncDisposable.DisposeAsync()", op.Info.PatternDisposeMethod.ToTestDisplayString());
Assert.True(op.Info.DisposeArguments.IsEmpty);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncDisposable_07(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System.Threading;
using System.Threading.Tasks;
interface ICustomEnumerator
{
public int Current {get;}
public ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
interface IMyAsyncDisposable1
{
ValueTask DisposeAsync();
}
interface IMyAsyncDisposable2
{
ValueTask DisposeAsync();
}
ref struct S2 : ICustomEnumerator, IMyAsyncDisposable1, IMyAsyncDisposable2
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"ICustomEnumerator, IMyAsyncDisposable1, IMyAsyncDisposable2, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (55,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}").WithArguments("TEnumerator").WithLocation(55, 9)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
Assert.False(op.Info.NeedsDispose);
Assert.False(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncDisposable_08(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System;
using System.Threading;
using System.Threading.Tasks;
interface ICustomEnumerator
{
public int Current {get;}
public ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
interface IMyAsyncDisposable1
{
ValueTask DisposeAsync();
}
interface IMyAsyncDisposable2
{
ValueTask DisposeAsync();
}
ref struct S2 : ICustomEnumerator, IMyAsyncDisposable1, IMyAsyncDisposable2, IAsyncDisposable
{
ValueTask IMyAsyncDisposable1.DisposeAsync() => throw null;
ValueTask IMyAsyncDisposable2.DisposeAsync() => throw null;
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"ICustomEnumerator, IMyAsyncDisposable1, IMyAsyncDisposable2, IAsyncDisposable, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (59,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}").WithArguments("TEnumerator").WithLocation(59, 9)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
AssertEx.Equal("System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void AwaitForeach_IAsyncDisposable_09(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System;
using System.Threading;
using System.Threading.Tasks;
interface ICustomEnumerator
{
public int Current {get;}
public ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
ref struct S2 : ICustomEnumerator, IAsyncDisposable
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
class C
{
static async Task Main()
{
await Test<S1, S2>();
}
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"ICustomEnumerator, allows ref struct
{
await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (46,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable))
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in default(TEnumerable))
{
System.Console.Write(i);
}").WithArguments("TEnumerator").WithLocation(46, 9)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.True(info.IsAsynchronous);
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.True(op.Info.IsAsynchronous);
Assert.False(op.Info.NeedsDispose);
Assert.False(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Fact]
public void AwaitForeach_IAsyncDisposable_LanguageVersion_01()
{
var src1 = @"
using System;
using System.Threading;
using System.Threading.Tasks;
public struct S1
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
public ref struct S2 : IAsyncDisposable
{
public int Current => throw null;
public ValueTask DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
using System;
using System.Threading;
using System.Threading.Tasks;
class C
{
static async Task Main()
{
await foreach (var i in new S1())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (10,15): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(10, 15)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics(
// (10,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in new S1())
{
}").WithArguments("S2").WithLocation(10, 9)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics(
// (10,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"await foreach (var i in new S1())
{
}").WithArguments("S2").WithLocation(10, 9)
);
}
[Fact]
public void AwaitForeach_IAsyncDisposable_LanguageVersion_03()
{
var src1 = @"
using System;
using System.Threading;
using System.Threading.Tasks;
public struct S1
{
public S2 GetAsyncEnumerator(CancellationToken token = default)
{
return new S2();
}
}
public ref struct S2 : IAsyncDisposable
{
public int Current => throw null;
ValueTask IAsyncDisposable.DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
public struct S3
{
public S4 GetAsyncEnumerator(CancellationToken token = default)
{
return new S4();
}
}
public struct S4 : IAsyncDisposable
{
public int Current => throw null;
ValueTask IAsyncDisposable.DisposeAsync() => throw null;
public ValueTask<bool> MoveNextAsync() => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static async System.Threading.Tasks.Task Main()
{
await foreach (var i in new S1()) {}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (6,9): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// await foreach (var i in new S1()) {}
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "await foreach (var i in new S1()) {}").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 9),
// (6,15): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await foreach (var i in new S1()) {}
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(6, 15)
);
comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (14,24): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// public ref struct S2 : IAsyncDisposable
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "IAsyncDisposable").WithArguments("ref struct interfaces", "13.0").WithLocation(14, 24),
// (40,15): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await foreach (var i in new S1()) {}
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(40, 15)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics(
// (6,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1()) {}
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await foreach (var i in new S1()) {}").WithArguments("S2").WithLocation(6, 9)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics(
// (6,9): error CS4007: Instance of type 'S2' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in new S1()) {}
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await foreach (var i in new S1()) {}").WithArguments("S2").WithLocation(6, 9)
);
var src3 = @"
class C
{
static async System.Threading.Tasks.Task Main()
{
await foreach (var i in new S3()) {}
}
}
";
var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp3.VerifyEmitDiagnostics();
}
[Fact]
public void AwaitForeach_IAsyncDisposable_LanguageVersion_04()
{
var src = @"
using System;
using System.Threading;
using System.Threading.Tasks;
interface ICustomEnumerator
{
public int Current {get;}
public ValueTask<bool> MoveNextAsync();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
{
TEnumerator GetAsyncEnumerator(CancellationToken token = default);
}
class C
{
static async Task Test<TEnumerable, TEnumerator>()
where TEnumerable : IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : ICustomEnumerator, IAsyncDisposable, allows ref struct
{
await foreach (var i in default(TEnumerable)) {}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp.VerifyEmitDiagnostics(
// (13,85): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// interface IGetEnumerator<TEnumerator> where TEnumerator : ICustomEnumerator, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(13, 85),
// (21,65): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where TEnumerable : IGetEnumerator<TEnumerator>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(21, 65),
// (22,73): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where TEnumerator : ICustomEnumerator, IAsyncDisposable, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(22, 73),
// (24,15): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater.
// await foreach (var i in default(TEnumerable)) {}
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "foreach").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(24, 15)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics(
// (24,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable)) {}
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await foreach (var i in default(TEnumerable)) {}").WithArguments("TEnumerator").WithLocation(24, 9)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (24,9): error CS4007: Instance of type 'TEnumerator' cannot be preserved across 'await' or 'yield' boundary.
// await foreach (var i in default(TEnumerable)) {}
Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await foreach (var i in default(TEnumerable)) {}").WithArguments("TEnumerator").WithLocation(24, 9)
);
}
[Fact]
public void Foreach_IEnumerable_01()
{
var src = @"
using System.Collections;
ref struct S : IEnumerable
{
public IEnumerator GetEnumerator()
{
return Get123();
}
static IEnumerator Get123()
{
yield return 123;
}
}
class C
{
static void Main()
{
foreach (var i in new S())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 56 (0x38)
.maxstack 2
.locals init (System.Collections.IEnumerator V_0,
S V_1,
System.IDisposable V_2)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S""
IL_0009: call ""System.Collections.IEnumerator S.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001c
IL_0011: ldloc.0
IL_0012: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_0017: call ""void System.Console.Write(object)""
IL_001c: ldloc.0
IL_001d: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0022: brtrue.s IL_0011
IL_0024: leave.s IL_0037
}
finally
{
IL_0026: ldloc.0
IL_0027: isinst ""System.IDisposable""
IL_002c: stloc.2
IL_002d: ldloc.2
IL_002e: brfalse.s IL_0036
IL_0030: ldloc.2
IL_0031: callvirt ""void System.IDisposable.Dispose()""
IL_0036: endfinally
}
IL_0037: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation ( System.Collections.IEnumerator S.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object System.Collections.IEnumerator.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
CaptureIds: [1]
Block[B4] - Block
Predecessors (0)
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IConversionOperation (TryCast: True, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ExplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator S.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator S.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
public void Foreach_IEnumerable_02()
{
var src = @"
using System.Collections;
ref struct S : IEnumerable
{
public IEnumerator GetEnumerator()
{
return Get123();
}
static IEnumerator Get123()
{
yield return 123;
}
IEnumerator IEnumerable.GetEnumerator() => throw null;
}
class C
{
static void Main()
{
foreach (var i in new S())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 56 (0x38)
.maxstack 2
.locals init (System.Collections.IEnumerator V_0,
S V_1,
System.IDisposable V_2)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S""
IL_0009: call ""System.Collections.IEnumerator S.GetEnumerator()""
IL_000e: stloc.0
.try
{
IL_000f: br.s IL_001c
IL_0011: ldloc.0
IL_0012: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_0017: call ""void System.Console.Write(object)""
IL_001c: ldloc.0
IL_001d: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0022: brtrue.s IL_0011
IL_0024: leave.s IL_0037
}
finally
{
IL_0026: ldloc.0
IL_0027: isinst ""System.IDisposable""
IL_002c: stloc.2
IL_002d: ldloc.2
IL_002e: brfalse.s IL_0036
IL_0030: ldloc.2
IL_0031: callvirt ""void System.IDisposable.Dispose()""
IL_0036: endfinally
}
IL_0037: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation ( System.Collections.IEnumerator S.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object System.Collections.IEnumerator.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
CaptureIds: [1]
Block[B4] - Block
Predecessors (0)
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IConversionOperation (TryCast: True, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ExplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator S.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator S.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
public void Foreach_IEnumerable_03()
{
var src = @"
using System.Collections;
ref struct S : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator()
{
return Get123();
}
static IEnumerator Get123()
{
yield return 123;
}
}
struct S1 : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator()
{
return Get456();
}
static IEnumerator Get456()
{
yield return 456;
}
}
class C
{
static void Main()
{
Test1();
Test2();
}
static void Test1()
{
foreach (var i in new S())
{
System.Console.Write(i);
}
}
static void Test2()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123456" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test1",
@"
{
// Code size 62 (0x3e)
.maxstack 2
.locals init (System.Collections.IEnumerator V_0,
S V_1,
System.IDisposable V_2)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S""
IL_0009: constrained. ""S""
IL_000f: callvirt ""System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()""
IL_0014: stloc.0
.try
{
IL_0015: br.s IL_0022
IL_0017: ldloc.0
IL_0018: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_001d: call ""void System.Console.Write(object)""
IL_0022: ldloc.0
IL_0023: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0028: brtrue.s IL_0017
IL_002a: leave.s IL_003d
}
finally
{
IL_002c: ldloc.0
IL_002d: isinst ""System.IDisposable""
IL_0032: stloc.2
IL_0033: ldloc.2
IL_0034: brfalse.s IL_003c
IL_0036: ldloc.2
IL_0037: callvirt ""void System.IDisposable.Dispose()""
IL_003c: endfinally
}
IL_003d: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test1").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IInvocationOperation (virtual System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: 'new S()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object System.Collections.IEnumerator.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
CaptureIds: [1]
Block[B4] - Block
Predecessors (0)
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S()')
Value:
IConversionOperation (TryCast: True, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ExplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 'new S()')
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'new S()')
Operand:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new S()')
Instance Receiver:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 'new S()')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().First();
AssertEx.Equal("new S()", foreachSyntax.Expression.ToString());
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerable_04(bool addStructConstraint)
{
var src = @"
using System.Collections;
ref struct S : IEnumerable
{
public IEnumerator GetEnumerator()
{
return Get123();
}
static IEnumerator Get123()
{
yield return 123;
}
}
class C
{
static void Main()
{
Test(new S());
}
static void Test<T>(T t) where T : " + (addStructConstraint ? "struct, " : "") + @"IEnumerable, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<T>(T)",
@"
{
// Code size 55 (0x37)
.maxstack 1
.locals init (System.Collections.IEnumerator V_0,
System.IDisposable V_1)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""T""
IL_0008: callvirt ""System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_001b
IL_0010: ldloc.0
IL_0011: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_0016: call ""void System.Console.Write(object)""
IL_001b: ldloc.0
IL_001c: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0021: brtrue.s IL_0010
IL_0023: leave.s IL_0036
}
finally
{
IL_0025: ldloc.0
IL_0026: isinst ""System.IDisposable""
IL_002b: stloc.1
IL_002c: ldloc.1
IL_002d: brfalse.s IL_0035
IL_002f: ldloc.1
IL_0030: callvirt ""void System.IDisposable.Dispose()""
IL_0035: endfinally
}
IL_0036: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object System.Collections.IEnumerator.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
CaptureIds: [1]
Block[B4] - Block
Predecessors (0)
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IConversionOperation (TryCast: True, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ExplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerable_05(bool addStructConstraint)
{
var src = @"
using System.Collections;
interface IMyEnumerable
{
IEnumerator GetEnumerator();
}
ref struct S : IMyEnumerable
{
public IEnumerator GetEnumerator()
{
return Get123();
}
static IEnumerator Get123()
{
yield return 123;
}
}
class C
{
static void Main()
{
Test(new S());
}
static void Test<T>(T t) where T : " + (addStructConstraint ? "struct, " : "") + @"IMyEnumerable, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Test<T>(T)",
@"
{
// Code size 55 (0x37)
.maxstack 1
.locals init (System.Collections.IEnumerator V_0,
System.IDisposable V_1)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""T""
IL_0008: callvirt ""System.Collections.IEnumerator IMyEnumerable.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_001b
IL_0010: ldloc.0
IL_0011: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_0016: call ""void System.Console.Write(object)""
IL_001b: ldloc.0
IL_001c: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0021: brtrue.s IL_0010
IL_0023: leave.s IL_0036
}
finally
{
IL_0025: ldloc.0
IL_0026: isinst ""System.IDisposable""
IL_002b: stloc.1
IL_002c: ldloc.1
IL_002d: brfalse.s IL_0035
IL_002f: ldloc.1
IL_0030: callvirt ""void System.IDisposable.Dispose()""
IL_0035: endfinally
}
IL_0036: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual System.Collections.IEnumerator IMyEnumerable.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object System.Collections.IEnumerator.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
CaptureIds: [1]
Block[B4] - Block
Predecessors (0)
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IConversionOperation (TryCast: True, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ExplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator IMyEnumerable.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator IMyEnumerable.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerable_06(bool addStructConstraint)
{
var src = @"
using System.Collections;
interface IMyEnumerable1
{
IEnumerator GetEnumerator();
}
interface IMyEnumerable2
{
IEnumerator GetEnumerator();
}
ref struct S : IMyEnumerable1, IMyEnumerable2
{
public IEnumerator GetEnumerator()
{
return Get123();
}
static IEnumerator Get123()
{
yield return 123;
}
}
class C
{
static void Main()
{
Test(new S());
}
static void Test<T>(T t) where T : " + (addStructConstraint ? "struct, " : "") + @"IMyEnumerable1, IMyEnumerable2, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (36,27): warning CS0278: 'T' does not implement the 'collection' pattern. 'IMyEnumerable1.GetEnumerator()' is ambiguous with 'IMyEnumerable2.GetEnumerator()'.
// foreach (var i in t)
Diagnostic(ErrorCode.WRN_PatternIsAmbiguous, "t").WithArguments("T", "collection", "IMyEnumerable1.GetEnumerator()", "IMyEnumerable2.GetEnumerator()").WithLocation(36, 27),
// (36,27): error CS1579: foreach statement cannot operate on variables of type 'T' because 'T' does not contain a public instance or extension definition for 'GetEnumerator'
// foreach (var i in t)
Diagnostic(ErrorCode.ERR_ForEachMissingMember, "t").WithArguments("T", "GetEnumerator").WithLocation(36, 27)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
Assert.Null(info.GetEnumeratorMethod);
Assert.Null(info.ElementType);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.Null(op.Info);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerable_07(bool addStructConstraint)
{
var src = @"
using System.Collections;
interface IMyEnumerable1
{
IEnumerator GetEnumerator();
}
interface IMyEnumerable2
{
IEnumerator GetEnumerator();
}
ref struct S : IMyEnumerable1, IMyEnumerable2, IEnumerable
{
IEnumerator IMyEnumerable1.GetEnumerator() => throw null;
IEnumerator IMyEnumerable2.GetEnumerator() => throw null;
public IEnumerator GetEnumerator()
{
return Get123();
}
static IEnumerator Get123()
{
yield return 123;
}
}
class C
{
static void Main()
{
Test(new S());
}
static void Test<T>(T t) where T : " + (addStructConstraint ? "struct, " : "") + @"IMyEnumerable1, IMyEnumerable2, IEnumerable, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(
// (39,27): warning CS0278: 'T' does not implement the 'collection' pattern. 'IMyEnumerable1.GetEnumerator()' is ambiguous with 'IMyEnumerable2.GetEnumerator()'.
// foreach (var i in t)
Diagnostic(ErrorCode.WRN_PatternIsAmbiguous, "t").WithArguments("T", "collection", "IMyEnumerable1.GetEnumerator()", "IMyEnumerable2.GetEnumerator()").WithLocation(39, 27)
);
verifier.VerifyIL("C.Test<T>(T)",
@"
{
// Code size 55 (0x37)
.maxstack 1
.locals init (System.Collections.IEnumerator V_0,
System.IDisposable V_1)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""T""
IL_0008: callvirt ""System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_001b
IL_0010: ldloc.0
IL_0011: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_0016: call ""void System.Console.Write(object)""
IL_001b: ldloc.0
IL_001c: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0021: brtrue.s IL_0010
IL_0023: leave.s IL_0036
}
finally
{
IL_0025: ldloc.0
IL_0026: isinst ""System.IDisposable""
IL_002b: stloc.1
IL_002c: ldloc.1
IL_002d: brfalse.s IL_0035
IL_002f: ldloc.1
IL_0030: callvirt ""void System.IDisposable.Dispose()""
IL_0035: endfinally
}
IL_0036: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IInvocationOperation (virtual System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()) (OperationKind.Invocation, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B2]
Entering: {R2} {R3}
.try {R2, R3}
{
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B7]
IInvocationOperation (virtual System.Boolean System.Collections.IEnumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Arguments(0)
Finalizing: {R5}
Leaving: {R3} {R2} {R1}
Next (Regular) Block[B3]
Entering: {R4}
.locals {R4}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object System.Collections.IEnumerator.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R4}
}
}
.finally {R5}
{
CaptureIds: [1]
Block[B4] - Block
Predecessors (0)
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 't')
Value:
IConversionOperation (TryCast: True, Unchecked) (OperationKind.Conversion, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
(ExplicitReference)
Operand:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.IEnumerator, IsImplicit) (Syntax: 't')
Jump if True (Regular) to Block[B6]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 't')
Operand:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Next (Regular) Block[B5]
Block[B5] - Block
Predecessors: [B4]
Statements (1)
IInvocationOperation (virtual void System.IDisposable.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 't')
Instance Receiver:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.IDisposable, IsImplicit) (Syntax: 't')
Arguments(0)
Next (Regular) Block[B6]
Block[B6] - Block
Predecessors: [B4] [B5]
Statements (0)
Next (StructuredExceptionHandling) Block[null]
}
}
Block[B7] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()", op.Info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Empty(op.Info.GetEnumeratorArguments);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
}
[Fact]
public void Foreach_IEnumerable_LanguageVersion_01()
{
var src1 = @"
using System.Collections;
public ref struct S : IEnumerable
{
public IEnumerator GetEnumerator() => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Main()
{
foreach (var i in new S())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IEnumerable_LanguageVersion_03()
{
var src1 = @"
using System.Collections;
public ref struct S : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() => throw null;
}
public struct S1 : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() => throw null;
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Main()
{
foreach (var i in new S())
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (6,27): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// foreach (var i in new S())
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "new S()").WithArguments("ref struct interfaces", "13.0").WithLocation(6, 27)
);
comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (4,23): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// public ref struct S : IEnumerable
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "IEnumerable").WithArguments("ref struct interfaces", "13.0").WithLocation(4, 23)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
var src3 = @"
class C
{
static void Main()
{
foreach (var i in new S1())
{
}
}
}
";
var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp3.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IEnumerable_LanguageVersion_04()
{
var src = @"
using System.Collections;
class C
{
static void Test<T>(T t) where T : IEnumerable, allows ref struct
{
foreach (var i in t)
{
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp.VerifyEmitDiagnostics(
// (6,60): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static void Test<T>(T t) where T : IEnumerable, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(6, 60)
);
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp.VerifyEmitDiagnostics();
comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void Foreach_IEnumerable_LanguageVersion_05()
{
var src1 = @"
using System.Collections;
public interface IMyEnumerable
{
IEnumerator GetEnumerator();
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var src2 = @"
class C
{
static void Test<T>(T t) where T : IMyEnumerable, allows ref struct
{
foreach (var i in t)
{
}
}
}
";
var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (4,62): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static void Test<T>(T t) where T : IMyEnumerable, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(4, 62)
);
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerator_01(bool s1IsRefStruct)
{
var src = @"
using System.Collections;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator
{
bool stop;
public object Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 39 (0x27)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""object S2.Current.get""
IL_0018: call ""void System.Console.Write(object)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B4]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Leaving: {R1}
Next (Regular) Block[B3]
Entering: {R2}
.locals {R2}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object S2.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R2}
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Object S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Object S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.False(op.Info.NeedsDispose);
Assert.False(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerator_02(bool s1IsRefStruct)
{
var src = @"
using System.Collections;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator
{
bool stop;
public object Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
object System.Collections.IEnumerator.Current => throw null;
bool System.Collections.IEnumerator.MoveNext() => throw null;
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 39 (0x27)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""object S2.Current.get""
IL_0018: call ""void System.Console.Write(object)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B4]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Leaving: {R1}
Next (Regular) Block[B3]
Entering: {R2}
.locals {R2}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object S2.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R2}
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Object S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Object S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.False(op.Info.NeedsDispose);
Assert.False(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerator_03(bool s1IsRefStruct, bool currentIsPublic, bool moveNextIsPublic)
{
if (currentIsPublic && moveNextIsPublic)
{
return;
}
var src = @"
using System.Collections;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator
{
bool stop;
" + (currentIsPublic ? "public object " : "object IEnumerator.") + @"Current => 123;
" + (moveNextIsPublic ? "public bool " : "bool System.Collections.IEnumerator.") + @"MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
}
class C
{
static void Main()
{
#line 100
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
if (!currentIsPublic)
{
comp.VerifyDiagnostics(
// (100,27): error CS0117: 'S2' does not contain a definition for 'Current'
// foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_NoSuchMember, "new S1()").WithArguments("S2", "Current").WithLocation(100, 27),
// (100,27): error CS0202: foreach requires that the return type 'S2' of 'S1.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property
// foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new S1()").WithArguments("S2", "S1.GetEnumerator()").WithLocation(100, 27)
);
}
else
{
Assert.False(moveNextIsPublic);
comp.VerifyDiagnostics(
// (100,27): error CS0117: 'S2' does not contain a definition for 'MoveNext'
// foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_NoSuchMember, "new S1()").WithArguments("S2", "MoveNext").WithLocation(100, 27),
// (100,27): error CS0202: foreach requires that the return type 'S2' of 'S1.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property
// foreach (var i in new S1())
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new S1()").WithArguments("S2", "S1.GetEnumerator()").WithLocation(100, 27)
);
}
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(1):
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1, IsInvalid) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Next (Regular) Block[B2]
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B4]
IInvalidOperation (OperationKind.Invalid, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(1):
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(0)
Next (Regular) Block[B3]
Entering: {R1}
.locals {R1}
{
Locals: [var i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: var, IsImplicit) (Syntax: 'var')
Right:
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(1):
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'new S1()')
Children(0)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvalidOperation (OperationKind.Invalid, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Children(2):
IOperation: (OperationKind.None, Type: System.Console) (Syntax: 'System.Console')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: var) (Syntax: 'i')
Next (Regular) Block[B2]
Leaving: {R1}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
Assert.Null(info.ElementType);
Assert.Null(info.MoveNextMethod);
Assert.Null(info.CurrentProperty);
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.Null(op.Info);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerator_04(bool s1IsRefStruct, bool addExplicitImplementationOfCurrentAndMoveNext)
{
var src = @"
using System.Collections;
" + (s1IsRefStruct ? "ref " : "") + @"struct S1
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator
{
bool stop;
public object Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
" +
(addExplicitImplementationOfCurrentAndMoveNext ?
@"
object IEnumerator.Current => throw null;
bool System.Collections.IEnumerator.MoveNext() => throw null;
"
:
"") +
@"
}
class C
{
static void Main()
{
foreach (var i in new S1())
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C.Main",
@"
{
// Code size 39 (0x27)
.maxstack 2
.locals init (S2 V_0,
S1 V_1)
IL_0000: ldloca.s V_1
IL_0002: dup
IL_0003: initobj ""S1""
IL_0009: call ""S2 S1.GetEnumerator()""
IL_000e: stloc.0
IL_000f: br.s IL_001d
IL_0011: ldloca.s V_0
IL_0013: call ""object S2.Current.get""
IL_0018: call ""void System.Console.Write(object)""
IL_001d: ldloca.s V_0
IL_001f: call ""bool S2.MoveNext()""
IL_0024: brtrue.s IL_0011
IL_0026: ret
}
");
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Main").Single();
VerifyFlowGraph(comp, node, """
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new S1()')
Value:
IInvocationOperation ( S2 S1.GetEnumerator()) (OperationKind.Invocation, Type: S2, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S1, IsImplicit) (Syntax: 'new S1()')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(Identity)
Operand:
IObjectCreationOperation (Constructor: S1..ctor()) (OperationKind.ObjectCreation, Type: S1) (Syntax: 'new S1()')
Arguments(0)
Initializer:
null
Arguments(0)
Next (Regular) Block[B2]
Block[B2] - Block
Predecessors: [B1] [B3]
Statements (0)
Jump if False (Regular) to Block[B4]
IInvocationOperation ( System.Boolean S2.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new S1()')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
Arguments(0)
Leaving: {R1}
Next (Regular) Block[B3]
Entering: {R2}
.locals {R2}
{
Locals: [System.Object i]
Block[B3] - Block
Predecessors: [B2]
Statements (2)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var')
Left:
ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Right:
IPropertyReferenceOperation: System.Object S2.Current { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'var')
Instance Receiver:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S2, IsImplicit) (Syntax: 'new S1()')
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'System.Console.Write(i);')
Expression:
IInvocationOperation (void System.Console.Write(System.Object? value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'System.Console.Write(i)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Object) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Next (Regular) Block[B2]
Leaving: {R2}
}
}
Block[B4] - Exit
Predecessors: [B2]
Statements (0)
""");
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Object S2.Current { get; }", info.CurrentProperty.ToTestDisplayString());
Assert.Null(info.DisposeMethod);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean S2.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Object S2.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.False(op.Info.NeedsDispose);
Assert.False(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerator_05(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System.Collections;
interface IGetEnumerator<TEnumerator> where TEnumerator : IEnumerator, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IEnumerator, System.IDisposable
{
bool stop;
public object Current => 123;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"IEnumerator, System.IDisposable, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"123D" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } :
Verification.Skipped).VerifyDiagnostics();
if (addStructConstraintToTEnumerator)
{
verifier.VerifyIL("C.Test<TEnumerable, TEnumerator>(TEnumerable)",
@"
{
// Code size 66 (0x42)
.maxstack 1
.locals init (TEnumerator V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""TEnumerable""
IL_0008: callvirt ""TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_0022
IL_0010: ldloca.s V_0
IL_0012: constrained. ""TEnumerator""
IL_0018: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_001d: call ""void System.Console.Write(object)""
IL_0022: ldloca.s V_0
IL_0024: constrained. ""TEnumerator""
IL_002a: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_002f: brtrue.s IL_0010
IL_0031: leave.s IL_0041
}
finally
{
IL_0033: ldloca.s V_0
IL_0035: constrained. ""TEnumerator""
IL_003b: callvirt ""void System.IDisposable.Dispose()""
IL_0040: endfinally
}
IL_0041: ret
}
");
}
else
{
verifier.VerifyIL("C.Test<TEnumerable, TEnumerator>(TEnumerable)",
@"
{
// Code size 74 (0x4a)
.maxstack 1
.locals init (TEnumerator V_0)
IL_0000: ldarga.s V_0
IL_0002: constrained. ""TEnumerable""
IL_0008: callvirt ""TEnumerator IGetEnumerator<TEnumerator>.GetEnumerator()""
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_0022
IL_0010: ldloca.s V_0
IL_0012: constrained. ""TEnumerator""
IL_0018: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_001d: call ""void System.Console.Write(object)""
IL_0022: ldloca.s V_0
IL_0024: constrained. ""TEnumerator""
IL_002a: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_002f: brtrue.s IL_0010
IL_0031: leave.s IL_0049
}
finally
{
IL_0033: ldloc.0
IL_0034: box ""TEnumerator""
IL_0039: brfalse.s IL_0048
IL_003b: ldloca.s V_0
IL_003d: constrained. ""TEnumerator""
IL_0043: callvirt ""void System.IDisposable.Dispose()""
IL_0048: endfinally
}
IL_0049: ret
}
");
}
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
AssertEx.Equal("System.Object", info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", info.MoveNextMethod.ToTestDisplayString());
AssertEx.Equal("System.Object System.Collections.IEnumerator.Current { get; }", info.CurrentProperty.ToTestDisplayString());
AssertEx.Equal("void System.IDisposable.Dispose()", info.DisposeMethod.ToTestDisplayString());
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.False(op.Info.IsAsynchronous);
AssertEx.Equal("System.Object", op.Info.ElementType.ToTestDisplayString());
AssertEx.Equal("System.Boolean System.Collections.IEnumerator.MoveNext()", op.Info.MoveNextMethod.ToTestDisplayString());
Assert.Empty(op.Info.MoveNextArguments);
AssertEx.Equal("System.Object System.Collections.IEnumerator.Current { get; }", op.Info.CurrentProperty.ToTestDisplayString());
Assert.True(op.Info.CurrentArguments.IsDefault);
Assert.True(op.Info.NeedsDispose);
Assert.True(op.Info.KnownToImplementIDisposable);
Assert.Null(op.Info.PatternDisposeMethod);
Assert.True(op.Info.DisposeArguments.IsDefault);
}
[Theory]
[CombinatorialData]
public void Foreach_IEnumerator_09(bool s1IsRefStruct, bool addStructConstraintToTEnumerable, bool addStructConstraintToTEnumerator)
{
var src = @"
using System.Collections;
interface IMyEnumerator1<T>
{
T Current {get;}
bool MoveNext();
}
interface IMyEnumerator2<T>
{
T Current {get;}
bool MoveNext();
}
interface IGetEnumerator<TEnumerator> where TEnumerator : IMyEnumerator1<int>, IMyEnumerator2<int>, IEnumerator, allows ref struct
{
TEnumerator GetEnumerator();
}
" + (s1IsRefStruct ? "ref " : "") + @"struct S1 : IGetEnumerator<S2>
{
public S2 GetEnumerator()
{
return new S2();
}
}
ref struct S2 : IMyEnumerator1<int>, IMyEnumerator2<int>, IEnumerator
{
bool stop;
public int Current => 123;
object System.Collections.IEnumerator.Current => Current;
public bool MoveNext()
{
if (!stop)
{
stop = true;
return true;
}
return false;
}
public void Reset() { }
public void Dispose()
{
System.Console.Write('D');
}
}
class C
{
static void Main()
{
Test<S1, S2>(new S1());
}
static void Test<TEnumerable, TEnumerator>(TEnumerable t)
where TEnumerable : " + (addStructConstraintToTEnumerable ? "struct, " : "") + @"IGetEnumerator<TEnumerator>, allows ref struct
where TEnumerator : " + (addStructConstraintToTEnumerator ? "struct, " : "") + @"IMyEnumerator1<int>, IMyEnumerator2<int>, IEnumerator, allows ref struct
{
foreach (var i in t)
{
System.Console.Write(i);
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (62,27): error CS0202: foreach requires that the return type 'TEnumerator' of 'IGetEnumerator<TEnumerator>.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property
// foreach (var i in t)
Diagnostic(ErrorCode.ERR_BadGetEnumerator, "t").WithArguments("TEnumerator", "IGetEnumerator<TEnumerator>.GetEnumerator()").WithLocation(62, 27)
);
var tree = comp.SyntaxTrees.Single();
var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test").Single();
var model = comp.GetSemanticModel(tree);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.False(info.IsAsynchronous);
Assert.Null(info.ElementType);
Assert.Null(info.MoveNextMethod);
Assert.Null(info.CurrentProperty);
var op = (Operations.ForEachLoopOperation)model.GetOperation(foreachSyntax);
Assert.Null(op.Info);
}
[Fact]
public void ConstraintsCheck_01()
{
var src = @"
ref struct S1
{
}
class C
{
static void Main()
{
Test(new S1());
}
static void Test<T>(T x)
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (10,9): error CS9244: The type 'S1' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'C.Test<T>(T)'
// Test(new S1());
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "Test").WithArguments("C.Test<T>(T)", "T", "S1").WithLocation(10, 9)
);
}
[Fact]
public void ConstraintsCheck_02()
{
var src1 = @"
public class Helper
{
#line 100
public static void Test<T>(T x) where T : allows ref struct
{
System.Console.Write(""Called"");
}
}
";
var src2 = @"
ref struct S1
{
}
class C
{
static void Main()
{
#line 200
Helper.Test(new S1());
}
}
";
var comp = CreateCompilation([src1, src2], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"Called" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CreateCompilation([src1, src2], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation([src1, src2], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (100,54): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public static void Test<T>(T x) where T : allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(100, 54)
);
var comp1Ref = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics).ToMetadataReference();
comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"Called" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CreateCompilation(src2, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation(src2, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (200,9): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// Helper.Test(new S1());
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "Helper.Test").WithArguments("allows ref struct constraint", "13.0").WithLocation(200, 9)
);
CreateCompilation([src1, src2], targetFramework: TargetFramework.Mscorlib461Extended, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (100,54): error CS9240: Target runtime doesn't support by-ref-like generics.
// public static void Test<T>(T x) where T : allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(100, 54)
);
var src3 = @"
struct S1
{
}
class C
{
static void Main()
{
#line 200
Helper.Test(new S1());
}
}
";
comp = CreateCompilation([src1, src3], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"Called" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CreateCompilation([src1, src3], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation([src1, src3], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (100,54): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public static void Test<T>(T x) where T : allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(100, 54)
);
comp = CreateCompilation(src3, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"Called" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CreateCompilation(src3, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation(src3, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics();
CreateCompilation([src1, src3], targetFramework: TargetFramework.Mscorlib461Extended, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (100,54): error CS9240: Target runtime doesn't support by-ref-like generics.
// public static void Test<T>(T x) where T : allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(100, 54)
);
comp1Ref = CreateCompilation(src1, targetFramework: TargetFramework.Mscorlib461Extended).ToMetadataReference();
CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Mscorlib461Extended, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (200,9): error CS9240: Target runtime doesn't support by-ref-like generics.
// Helper.Test(new S1());
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "Helper.Test").WithLocation(200, 9)
);
CreateCompilation(src3, references: [comp1Ref], targetFramework: TargetFramework.Mscorlib461Extended, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics();
}
[Fact]
public void ConstraintsCheck_03()
{
var src = @"
ref struct S1
{
}
class C
{
static void Test1<T>(T x) where T : allows ref struct
{
Test2(x);
Test2<T>(x);
}
static void Test2<T>(T x)
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,9): error CS9244: The type 'T' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'C.Test2<T>(T)'
// Test2(x);
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "Test2").WithArguments("C.Test2<T>(T)", "T", "T").WithLocation(10, 9),
// (11,9): error CS9244: The type 'T' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'C.Test2<T>(T)'
// Test2<T>(x);
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "Test2<T>").WithArguments("C.Test2<T>(T)", "T", "T").WithLocation(11, 9)
);
}
[Fact]
public void ConstraintsCheck_04()
{
var src = @"
ref struct S1
{
}
class C
{
static void Main()
{
Test2((byte)2);
Test3((int)3);
Test3(new S1());
Test4((long)4);
}
static void Test1<T>(T x) where T : allows ref struct
{
System.Console.WriteLine(""Called {0}"", typeof(T));
}
static void Test2<T>(T x)
{
Test1(x);
Test1<T>(x);
}
static void Test3<T>(T x) where T : allows ref struct
{
Test1(x);
Test1<T>(x);
}
static void Test4<T>(T x)
{
Test2(x);
Test2<T>(x);
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: !ExecutionConditionUtil.IsMonoOrCoreClr ? null : @"
Called System.Byte
Called System.Byte
Called System.Int32
Called System.Int32
Called S1
Called S1
Called System.Int64
Called System.Int64
Called System.Int64
Called System.Int64", verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void ConstraintsCheck_05()
{
var src = @"
ref struct S1
{
}
class C<T, S>
where T : allows ref struct
where S : T
{
static void Main()
{
_ = typeof(C<S1, S1>);
_ = typeof(C<int, int>);
_ = typeof(C<object, object>);
_ = typeof(C<object, string>);
_ = typeof(C<T, T>);
_ = typeof(C<S, S>);
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (12,26): error CS9244: The type 'S1' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'S' in the generic type or method 'C<T, S>'
// _ = typeof(C<S1, S1>);
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "S1").WithArguments("C<T, S>", "S", "S1").WithLocation(12, 26),
// (16,25): error CS9244: The type 'T' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'S' in the generic type or method 'C<T, S>'
// _ = typeof(C<T, T>);
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "T").WithArguments("C<T, S>", "S", "T").WithLocation(16, 25)
);
}
[Fact]
public void ConstraintsCheck_06()
{
var src = @"
#nullable enable
class C
{
static void Test1<T>(T? x) where T : allows ref struct
{
}
static void Test2<T>(T? x) where T : struct, allows ref struct
{
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,29): error CS9244: The type 'T' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'Nullable<T>'
// static void Test2<T>(T? x) where T : struct, allows ref struct
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "x").WithArguments("System.Nullable<T>", "T", "T").WithLocation(10, 29)
);
}
[Fact]
public void ConstraintsCheck_07()
{
var src1 = @"
#line 100
public class Helper<T> where T : allows ref struct
{
public static void Test(T x)
{
System.Console.Write(""Called"");
}
}
";
var src2 = @"
ref struct S1
{
}
class C
{
static void Main()
{
#line 200
Helper<S1>.Test(new S1());
}
}
";
var comp = CreateCompilation([src1, src2], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"Called" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CreateCompilation([src1, src2], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation([src1, src2], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (100,41): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public class Helper<T> where T : allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(100, 41)
);
var comp1Ref = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics).ToMetadataReference();
comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"Called" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CreateCompilation(src2, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation(src2, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (200,16): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// Helper<S1>.Test(new S1());
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "S1").WithArguments("allows ref struct constraint", "13.0").WithLocation(200, 16)
);
CreateCompilation([src1, src2], targetFramework: TargetFramework.Mscorlib461Extended, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (100,41): error CS9240: Target runtime doesn't support by-ref-like generics.
// public class Helper<T> where T : allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(100, 41)
);
var src3 = @"
struct S1
{
}
class C
{
static void Main()
{
#line 200
Helper<S1>.Test(new S1());
}
}
";
comp = CreateCompilation([src1, src3], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"Called" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CreateCompilation([src1, src3], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation([src1, src3], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12).VerifyDiagnostics(
// (100,41): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public class Helper<T> where T : allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(100, 41)
);
comp = CreateCompilation(src3, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"Called" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
CreateCompilation(src3, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13).VerifyEmitDiagnostics();
CreateCompilation(src3, references: [comp1Ref], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics();
CreateCompilation([src1, src3], targetFramework: TargetFramework.Mscorlib461Extended, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (100,41): error CS9240: Target runtime doesn't support by-ref-like generics.
// public class Helper<T> where T : allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(100, 41)
);
comp1Ref = CreateCompilation(src1, targetFramework: TargetFramework.Mscorlib461Extended).ToMetadataReference();
CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Mscorlib461Extended, options: TestOptions.ReleaseExe).VerifyDiagnostics(
// (200,16): error CS9240: Target runtime doesn't support by-ref-like generics.
// Helper<S1>.Test(new S1());
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "S1").WithLocation(200, 16)
);
CreateCompilation(src3, references: [comp1Ref], targetFramework: TargetFramework.Mscorlib461Extended, options: TestOptions.ReleaseExe).VerifyEmitDiagnostics();
}
[Fact]
public void IdentityConversion_01()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static T Test1(T h1)
{
return h1;
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
Helper1<Program>.Test1(new Program());
Helper1<Program>.Test1(null);
System.Console.Write(""Done"");
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "Done" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}
");
}
[Fact]
public void IdentityConversion_02()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static T Test1(T h1)
{
return (T)h1;
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
Helper1<Program>.Test1(new Program());
Helper1<Program>.Test1(null);
System.Console.Write(""Done"");
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "Done" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}
");
}
[Fact]
public void IdentityConversion_03()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
public static T Test1(T h1)
{
return h1;
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
System.Console.Write(""Done"");
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "Done" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}
");
}
[Fact]
public void IdentityConversion_04()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
public static T Test1(T h1)
{
return (T)h1;
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
System.Console.Write(""Done"");
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "Done" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}
");
}
[Fact]
public void IllegalBoxing_01()
{
var src = @"
public class Helper
{
public static object Test1<T>(T x) where T : allows ref struct
{
return x;
}
public static object Test2<T>(T x) where T : allows ref struct
{
return (object)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (6,16): error CS0029: Cannot implicitly convert type 'T' to 'object'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "object").WithLocation(6, 16),
// (11,16): error CS0030: Cannot convert type 'T' to 'object'
// return (object)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(object)x").WithArguments("T", "object").WithLocation(11, 16)
);
}
[Fact]
public void IllegalBoxing_02()
{
var src = @"
public interface I1
{
}
public class Helper
{
public static I1 Test1<T>(T x) where T : I1, allows ref struct
{
return x;
}
public static I1 Test2<T>(T x) where T : I1, allows ref struct
{
return (I1)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,16): error CS0029: Cannot implicitly convert type 'T' to 'I1'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "I1").WithLocation(10, 16),
// (15,16): error CS0030: Cannot convert type 'T' to 'I1'
// return (I1)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(I1)x").WithArguments("T", "I1").WithLocation(15, 16)
);
}
[Fact]
public void IllegalBoxing_03()
{
var src = @"
public interface I1
{
}
public class Helper
{
static U Test1<T, U>(T x)
where T : U, allows ref struct
{
return x;
}
static U Test2<T, U>(T x)
where T : U, allows ref struct
{
return (U)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (11,16): error CS0029: Cannot implicitly convert type 'T' to 'U'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "U").WithLocation(11, 16),
// (16,16): error CS0030: Cannot convert type 'T' to 'U'
// return (U)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(U)x").WithArguments("T", "U").WithLocation(16, 16)
);
}
[Fact]
public void IllegalBoxing_04()
{
var src = @"
ref struct S
{
}
public class Helper
{
static object Test1<T>(T x)
where T : struct, allows ref struct
{
return x;
}
static object Test2(S x)
{
return x;
}
static object Test3<T>(T x)
where T : struct, allows ref struct
{
return (object)x;
}
static object Test4(S x)
{
return (object)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (11,16): error CS0029: Cannot implicitly convert type 'T' to 'object'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "object").WithLocation(11, 16),
// (16,16): error CS0029: Cannot implicitly convert type 'S' to 'object'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("S", "object").WithLocation(16, 16),
// (22,16): error CS0030: Cannot convert type 'T' to 'object'
// return (object)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(object)x").WithArguments("T", "object").WithLocation(22, 16),
// (27,16): error CS0030: Cannot convert type 'S' to 'object'
// return (object)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(object)x").WithArguments("S", "object").WithLocation(27, 16)
);
}
[Fact]
public void IllegalBoxing_05()
{
var src = @"
interface I1 {}
ref struct S : I1
{
}
public class Helper
{
static I1 Test1<T>(T x)
where T : I1, allows ref struct
{
return x;
}
static I1 Test2(S x)
{
return x;
}
static I1 Test3<T>(T x)
where T : I1, allows ref struct
{
return (I1)x;
}
static I1 Test4(S x)
{
return (I1)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (13,16): error CS0029: Cannot implicitly convert type 'T' to 'I1'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "I1").WithLocation(13, 16),
// (18,16): error CS0029: Cannot implicitly convert type 'S' to 'I1'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("S", "I1").WithLocation(18, 16),
// (24,16): error CS0030: Cannot convert type 'T' to 'I1'
// return (I1)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(I1)x").WithArguments("T", "I1").WithLocation(24, 16),
// (29,16): error CS0030: Cannot convert type 'S' to 'I1'
// return (I1)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(I1)x").WithArguments("S", "I1").WithLocation(29, 16)
);
}
[Fact]
public void Unboxing_01()
{
var src = @"
public interface I1
{
}
ref struct S : I1
{
}
public class Helper
{
static S Test1(I1 x)
{
return x;
}
static S Test2(I1 x)
{
return (S)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (14,16): error CS0029: Cannot implicitly convert type 'I1' to 'S'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("I1", "S").WithLocation(14, 16),
// (19,16): error CS0030: Cannot convert type 'I1' to 'S'
// return (S)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(S)x").WithArguments("I1", "S").WithLocation(19, 16)
);
}
[Fact]
public void Unboxing_02()
{
var src = @"
public interface I1
{
}
ref struct S : I1
{
}
public class Helper
{
static S Test1<T>(T x)
where T : I1, allows ref struct
{
return x;
}
static S Test2<T>(T x)
where T : I1, allows ref struct
{
return (S)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (15,16): error CS0029: Cannot implicitly convert type 'T' to 'S'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "S").WithLocation(15, 16),
// (20,16): error CS0030: Cannot convert type 'T' to 'S'
// return (S)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(S)x").WithArguments("T", "S").WithLocation(20, 16)
);
}
[Fact]
public void Unboxing_03()
{
var src = @"
public interface I1
{
}
public class Helper
{
static U Test1<T, U>(T x)
where T : allows ref struct
where U : T
{
#line 100
return x;
}
static U Test2<T, U>(T x)
where T : allows ref struct
where U : T
{
#line 200
return (U)x;
// Not a legal IL according to https://github.com/dotnet/runtime/blob/main/docs/design/features/byreflike-generics.md
// IL_0000: ldarg.0
// IL_0001: box ""T""
// IL_0006: unbox.any ""U""
// IL_000b: ret
}
static U Test3<T, U>(T x)
where T : allows ref struct
where U : class, T
{
#line 300
return x;
}
static U Test4<T, U>(T x)
where T : allows ref struct
where U : class, T
{
#line 400
return (U)x;
// Not a legal IL according to https://github.com/dotnet/runtime/blob/main/docs/design/features/byreflike-generics.md
// IL_0000: ldarg.0
// IL_0001: box ""T""
// IL_0006: unbox.any ""U""
// IL_000b: ret
}
static T Test5<T, U>(U y)
where T : allows ref struct
where U : T
{
#line 500
return y;
}
static T Test6<T, U>(U y)
where T : allows ref struct
where U : T
{
#line 600
return (T)y;
// Not a legal IL according to https://github.com/dotnet/runtime/blob/main/docs/design/features/byreflike-generics.md
// IL_0000: ldarg.0
// IL_0001: box ""U""
// IL_0006: unbox.any ""T""
// IL_000b: ret
}
static T Test7<T, U>(U y)
where T : allows ref struct
where U : class, T
{
#line 700
return y;
}
static T Test8<T, U>(U y)
where T : allows ref struct
where U : class, T
{
#line 800
return (T)y;
// Not a legal IL according to https://github.com/dotnet/runtime/blob/main/docs/design/features/byreflike-generics.md
// IL_0000: ldarg.0
// IL_0001: box ""U""
// IL_0006: unbox.any ""T""
// IL_000b: ret
}
static U Test9<T, U>(T x)
where T : class
where U : T, allows ref struct
{
#line 900
return x;
}
static U Test10<T, U>(T x)
where T : class
where U : T, allows ref struct
{
#line 1000
return (U)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (100,16): error CS0029: Cannot implicitly convert type 'T' to 'U'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "U").WithLocation(100, 16),
// (200,16): error CS0030: Cannot convert type 'T' to 'U'
// return (U)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(U)x").WithArguments("T", "U").WithLocation(200, 16),
// (300,16): error CS0029: Cannot implicitly convert type 'T' to 'U'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "U").WithLocation(300, 16),
// (400,16): error CS0030: Cannot convert type 'T' to 'U'
// return (U)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(U)x").WithArguments("T", "U").WithLocation(400, 16),
// (500,16): error CS0029: Cannot implicitly convert type 'U' to 'T'
// return y;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("U", "T").WithLocation(500, 16),
// (600,16): error CS0030: Cannot convert type 'U' to 'T'
// return (T)y;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(T)y").WithArguments("U", "T").WithLocation(600, 16),
// (700,16): error CS0029: Cannot implicitly convert type 'U' to 'T'
// return y;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("U", "T").WithLocation(700, 16),
// (800,16): error CS0030: Cannot convert type 'U' to 'T'
// return (T)y;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(T)y").WithArguments("U", "T").WithLocation(800, 16),
// (900,16): error CS0029: Cannot implicitly convert type 'T' to 'U'
// return x;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "U").WithLocation(900, 16),
// (1000,16): error CS0030: Cannot convert type 'T' to 'U'
// return (U)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(U)x").WithArguments("T", "U").WithLocation(1000, 16)
);
}
[Fact]
public void Unboxing_04()
{
var src = @"
ref struct S
{
}
public class Helper
{
static T Test1<T>(object x)
where T : allows ref struct
{
return (T)x;
}
static S Test2(object x)
{
return (S)x;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (11,16): error CS0030: Cannot convert type 'object' to 'T'
// return (T)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(T)x").WithArguments("object", "T").WithLocation(11, 16),
// (16,16): error CS0030: Cannot convert type 'object' to 'S'
// return (S)x;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(S)x").WithArguments("object", "S").WithLocation(16, 16)
);
}
[Fact]
public void CallObjectMember()
{
var src = @"
public class Helper
{
static string Test1<T>(T x)
where T : allows ref struct
{
return x.ToString();
}
static string Test2(S y)
{
return y.ToString();
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,16): error CS0029: Cannot implicitly convert type 'T' to 'object'
// return x.ToString();
Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "object").WithLocation(7, 16),
// (12,16): error CS0029: Cannot implicitly convert type 'S' to 'System.ValueType'
// return y.ToString();
Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("S", "System.ValueType").WithLocation(12, 16)
);
}
[Fact]
public void AnonymousTypeMember_01()
{
var src = @"
public class Helper
{
static void Test<T>(T x)
where T : allows ref struct
{
var y = new { x };
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,23): error CS0828: Cannot assign 'T' to anonymous type property
// var y = new { x };
Diagnostic(ErrorCode.ERR_AnonymousTypePropertyAssignedBadValue, "x").WithArguments("T").WithLocation(7, 23)
);
}
[ConditionalFact(typeof(NoUsedAssembliesValidation))] // https://github.com/dotnet/roslyn/issues/73558: Follow up on used assemblies validation failure. Could be an artifact of https://github.com/dotnet/roslyn/issues/72945.
[WorkItem("https://github.com/dotnet/roslyn/issues/72945")]
[WorkItem("https://github.com/dotnet/roslyn/issues/73558")]
public void AnonymousTypeMember_02()
{
var src = @"
public class Helper
{
static void Test1<T>(MyEnumerable<T> outer, MyEnumerable<T> inner1, MyEnumerable<T> inner2)
where T : I1, allows ref struct
{
#line 100
var q = from x in outer join y in inner1 on x.P equals y.P
join z in inner2 on y.P equals z.P
select 1;
}
static void Test2(MyEnumerable<S1> outer, MyEnumerable<S1> inner1, MyEnumerable<S1> inner2)
{
#line 200
var q = from x in outer join y in inner1 on x.P equals y.P
join z in inner2 on y.P equals z.P
select 1;
}
}
class MyEnumerable<T>
where T : allows ref struct
{
public MyEnumerable<TResult> Join<TInner, TKey,TResult> (MyEnumerable<TInner> inner, MyFunc<T,TKey> outerKeySelector, MyFunc<TInner,TKey> innerKeySelector, MyFunc<T,TInner,TResult> resultSelector)
where TInner : allows ref struct
where TKey : allows ref struct
where TResult : allows ref struct
=> throw null;
public MyEnumerable<TResult> Select<TResult> (MyFunc<T,TResult> selector)
where TResult : allows ref struct
=> throw null;
}
delegate TResult MyFunc<in T,out TResult>(T arg)
where T : allows ref struct
where TResult : allows ref struct;
delegate TResult MyFunc<in T1,in T2,out TResult>(T1 arg1, T2 arg2)
where T1 : allows ref struct
where T2 : allows ref struct
where TResult : allows ref struct;
interface I1
{
int P {get;}
}
ref struct S1
{
public int P {get;set;}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// Errors are expected for both queries, but it is a pre-existing condition - https://github.com/dotnet/roslyn/issues/72945
);
}
[Fact]
public void Makeref()
{
var src = @"
public class Helper
{
static void Test1<T>(T x)
where T : allows ref struct
{
System.TypedReference tr = __makeref(x);
}
static void Test2(S y)
{
System.TypedReference tr = __makeref(y);
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,36): error CS1601: Cannot make reference to variable of type 'T'
// System.TypedReference tr = __makeref(x);
Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "__makeref(x)").WithArguments("T").WithLocation(7, 36),
// (12,36): error CS1601: Cannot make reference to variable of type 'S'
// System.TypedReference tr = __makeref(y);
Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "__makeref(y)").WithArguments("S").WithLocation(12, 36)
);
}
[Fact]
public void ScopedTypeParameter_01()
{
var src = @"
public class Helper
{
static void Test1<T>(scoped T x)
where T : allows ref struct
{
}
static void Test2(scoped S y)
{
}
static void Test3<T>(scoped T z)
{
}
static void Test4<T>()
{
var d = void (scoped T u) => {};
d(default);
}
static void Test5<T>()
{
void local5(scoped T u) {};
local5(default);
}
static void Test6()
{
void local6<T>(scoped T u) {};
local6<string>(default);
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (13,26): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only.
// static void Test3<T>(scoped T z)
Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped T z").WithLocation(13, 26),
// (19,23): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only.
// var d = void (scoped T u) => {};
Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped T u").WithLocation(19, 23),
// (25,21): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only.
// void local5(scoped T u) {};
Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped T u").WithLocation(25, 21),
// (31,24): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only.
// void local6<T>(scoped T u) {};
Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped T u").WithLocation(31, 24)
);
var src2 = @"
public class Helper
{
public static void Test1<T>(scoped T x)
where T : allows ref struct
{
}
public static void Test2<T>()
where T : allows ref struct
{
var d = void (scoped T u) => {};
d(default);
void local5(scoped T u) {};
local5(default);
}
static void Test3()
{
void local6<T>(scoped T u) where T : allows ref struct {};
local6<string>(default);
}
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var tree = comp2.SyntaxTrees.Single();
var model = comp2.GetSemanticModel(tree);
var lambda = tree.GetRoot().DescendantNodes().OfType<ParenthesizedLambdaExpressionSyntax>().Single();
var parameter = model.GetSymbolInfo(lambda).Symbol.GetSymbol<MethodSymbol>().Parameters[0];
AssertEx.Equal("scoped T u", parameter.ToTestDisplayString());
Assert.Equal(ScopedKind.ScopedValue, parameter.EffectiveScope);
var localFunctions = tree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().ToArray();
Assert.Equal(2, localFunctions.Length);
foreach (var localFunction in localFunctions)
{
parameter = model.GetDeclaredSymbol(localFunction).GetSymbol<MethodSymbol>().Parameters[0];
AssertEx.Equal("scoped T u", parameter.ToTestDisplayString());
Assert.Equal(ScopedKind.ScopedValue, parameter.EffectiveScope);
}
CompileAndVerify(
comp2, symbolValidator: validate, sourceSymbolValidator: validate,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
var p = m.GlobalNamespace.GetMember<MethodSymbol>("Helper.Test1").Parameters[0];
AssertEx.Equal("scoped T x", p.ToTestDisplayString());
Assert.Equal(ScopedKind.ScopedValue, p.EffectiveScope);
}
}
[Fact]
public void ScopedTypeParameter_02()
{
var src = @"
#pragma warning disable CS0219 // The variable 'x' is assigned but its value is never used
public class Helper
{
static void Test1<T>()
where T : allows ref struct
{
scoped T x = default;
}
static void Test2()
{
scoped S y = default;
}
static void Test3<T>()
{
scoped T z = default;
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (19,16): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only.
// scoped T z = default;
Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "T").WithLocation(19, 16)
);
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var declarator = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().First();
AssertEx.Equal("x = default", declarator.ToString());
var local = model.GetDeclaredSymbol(declarator).GetSymbol<LocalSymbol>();
AssertEx.Equal("T x", local.ToTestDisplayString());
Assert.Equal(ScopedKind.ScopedValue, local.Scope);
}
[Fact]
public void ScopedTypeParameter_03()
{
var src = @"
partial class Helper<T>
where T : allows ref struct
{
partial void M1(scoped T x);
partial void M1(T x){}
partial void M2(T x);
partial void M2(scoped T x){}
partial void M3(scoped T x);
partial void M3(scoped T x){}
partial void M4(T x);
partial void M4(T x){}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,18): error CS8988: The 'scoped' modifier of parameter 'x' doesn't match partial definition.
// partial void M1(T x){}
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "M1").WithArguments("x").WithLocation(7, 18),
// (11,18): error CS8988: The 'scoped' modifier of parameter 'x' doesn't match partial definition.
// partial void M2(scoped T x){}
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "M2").WithArguments("x").WithLocation(11, 18)
);
}
[Fact]
public void ScopedTypeParameter_04()
{
var src = @"
using System;
class Helper<T>
where T : allows ref struct
{
delegate void D1(scoped T x);
delegate void D2(T x);
static D1 d11 = M1;
static D1 d12 = M2;
static D2 d21 = M1;
static D2 d22 = M2;
static void M1(scoped T x) {}
static void M2(T x) {}
}
class Helper
{
delegate void D1(scoped Span<int> x);
delegate void D2(Span<int> x);
static D1 d11 = M1;
static D1 d12 = M2;
static D2 d21 = M1;
static D2 d22 = M2;
static void M1(scoped Span<int> x) {}
static void M2(Span<int> x) {}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void LiftedUnaryOperator_InvalidTypeArgument01()
{
var code = @"
struct S1<T>
where T : struct, allows ref struct
{
public static T operator+(S1<T> s1) => throw null;
static void Test()
{
S1<T>? s1 = default;
var s2 = +s1;
}
}
";
var comp = CreateCompilation(code, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,18): error CS0023: Operator '+' cannot be applied to operand of type 'S1<T>?'
// var s2 = +s1;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "+s1").WithArguments("+", "S1<T>?").WithLocation(10, 18)
);
}
[Fact]
public void RefField()
{
var src = @"
#pragma warning disable CS0169 // The field is never used
ref struct S1
{
}
ref struct S2<T>
where T : allows ref struct
{
ref S1 a;
ref T b;
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (11,5): error CS9050: A ref field cannot refer to a ref struct.
// ref S1 a;
Diagnostic(ErrorCode.ERR_RefFieldCannotReferToRefStruct, "ref S1").WithLocation(11, 5),
// (12,5): error CS9050: A ref field cannot refer to a ref struct.
// ref T b;
Diagnostic(ErrorCode.ERR_RefFieldCannotReferToRefStruct, "ref T").WithLocation(12, 5)
);
}
[Fact]
public void UnscopedRef_01()
{
var sourceA =
@"
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
public class Helper
{
static void Test1<T>([UnscopedRef] params T x)
where T : IEnumerable<int>, IAdd, new(), allows ref struct
{
}
static void Test2([UnscopedRef] params S y)
{
}
static void Test3<T>([UnscopedRef] params T z)
where T : IEnumerable<int>, IAdd, new()
{
}
}
interface IAdd
{
void Add(int x);
}
ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => throw null;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
public void Add(int x){}
}
";
var comp = CreateCompilation(sourceA, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (16,27): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default.
// static void Test3<T>([UnscopedRef] params T z)
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(16, 27)
);
var src2 = @"
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
public class Helper
{
public static void Test1<T>([UnscopedRef] params T x)
where T : IEnumerable<int>, IAdd, new(), allows ref struct
{
}
}
public interface IAdd
{
void Add(int x);
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(
comp2, symbolValidator: validate, sourceSymbolValidator: validate,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
var p = m.GlobalNamespace.GetMember<MethodSymbol>("Helper.Test1").Parameters[0];
AssertEx.Equal("params T x", p.ToTestDisplayString());
Assert.Equal(ScopedKind.None, p.EffectiveScope);
}
}
[Fact]
public void UnscopedRef_02()
{
var sourceA =
@"
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
public class Helper
{
static void Test1<T>([UnscopedRef] params scoped T x)
where T : IEnumerable<int>, IAdd, new(), allows ref struct
{
}
static void Test2([UnscopedRef] params scoped S y)
{
}
}
interface IAdd
{
void Add(int x);
}
ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => throw null;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
public void Add(int x){}
}
";
var comp = CreateCompilation(sourceA, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,27): error CS9066: UnscopedRefAttribute cannot be applied to parameters that have a 'scoped' modifier.
// static void Test1<T>([UnscopedRef] params scoped T x)
Diagnostic(ErrorCode.ERR_UnscopedScoped, "UnscopedRef").WithLocation(7, 27),
// (12,24): error CS9066: UnscopedRefAttribute cannot be applied to parameters that have a 'scoped' modifier.
// static void Test2([UnscopedRef] params scoped S y)
Diagnostic(ErrorCode.ERR_UnscopedScoped, "UnscopedRef").WithLocation(12, 24)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/73557")]
public void UnscopedRef_03()
{
var sourceA =
@"
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
public class Helper
{
static void Test1<T>([UnscopedRef] scoped T x)
where T : IEnumerable<int>, IAdd, new(), allows ref struct
{
}
static void Test2([UnscopedRef] scoped S y)
{
}
}
interface IAdd
{
void Add(int x);
}
ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => throw null;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
public void Add(int x){}
}
";
var comp = CreateCompilation(sourceA, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,27): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default.
// static void Test1<T>([UnscopedRef] scoped T x)
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(7, 27),
// (12,24): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default.
// static void Test2([UnscopedRef] scoped S y)
Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(12, 24)
);
}
[Fact]
public void ScopedByDefault_01()
{
var src =
@"
using System.Collections.Generic;
class Helper
{
public static void Test1<T>(params T x)
where T : IEnumerable<int>, IAdd, new(), allows ref struct
{
}
public static void Test2(params S y)
{
}
public static void Test3<T>(params T z)
where T : IEnumerable<int>, IAdd, new()
{
}
}
interface IAdd
{
void Add(int x);
}
ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => throw null;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
public void Add(int x){}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(
comp, symbolValidator: validate, sourceSymbolValidator: validate,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
var p = m.GlobalNamespace.GetMember<MethodSymbol>("Helper.Test1").Parameters[0];
AssertEx.Equal("params T x", p.ToTestDisplayString());
Assert.Equal(ScopedKind.ScopedValue, p.EffectiveScope);
p = m.GlobalNamespace.GetMember<MethodSymbol>("Helper.Test2").Parameters[0];
AssertEx.Equal("params S y", p.ToTestDisplayString());
Assert.Equal(ScopedKind.ScopedValue, p.EffectiveScope);
p = m.GlobalNamespace.GetMember<MethodSymbol>("Helper.Test3").Parameters[0];
AssertEx.Equal("params T z", p.ToTestDisplayString());
Assert.Equal(ScopedKind.None, p.EffectiveScope);
}
}
[Fact]
public void ScopedByDefault_02()
{
var src =
@"
using System.Collections.Generic;
class Helper
{
public static void Test1<T>()
where T : IEnumerable<int>, IAdd, new(), allows ref struct
{
var l1 = (params T x) => {};
}
public static void Test2()
{
var l2 = (params S y) => {};
}
public static void Test3<T>()
where T : IEnumerable<int>, IAdd, new()
{
var l3 = (params T z) => {};
}
}
interface IAdd
{
void Add(int x);
}
ref struct S : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => throw null;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
public void Add(int x){}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(
comp,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var lambdas = tree.GetRoot().DescendantNodes().OfType<ParenthesizedLambdaExpressionSyntax>().ToArray();
var p = model.GetDeclaredSymbol(lambdas[0].ParameterList.Parameters[0]).GetSymbol<ParameterSymbol>();
AssertEx.Equal("params T x", p.ToTestDisplayString());
Assert.Equal(ScopedKind.ScopedValue, p.EffectiveScope);
p = model.GetDeclaredSymbol(lambdas[1].ParameterList.Parameters[0]).GetSymbol<ParameterSymbol>();
AssertEx.Equal("params S y", p.ToTestDisplayString());
Assert.Equal(ScopedKind.ScopedValue, p.EffectiveScope);
p = model.GetDeclaredSymbol(lambdas[2].ParameterList.Parameters[0]).GetSymbol<ParameterSymbol>();
AssertEx.Equal("params T z", p.ToTestDisplayString());
Assert.Equal(ScopedKind.None, p.EffectiveScope);
}
[Fact]
public void SystemActivatorCreateInstance_01()
{
var sourceA =
@"
public class Helper
{
static void Test1<T>()
where T : new(), allows ref struct
{
_ = System.Activator.CreateInstance<T>();
}
static void Test2()
{
_ = System.Activator.CreateInstance<S>();
}
}
ref struct S
{
}
";
var comp = CreateCompilation(sourceA, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
}
[Fact]
public void SystemActivatorCreateInstance_02()
{
var sourceA =
@"
public class Helper
{
static void Test1<T>()
where T : new(), allows ref struct
{
_ = new T();
}
}
ref struct S
{
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() => default;
}
}
";
var comp = CreateCompilation(sourceA, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (7,13): error CS9244: The type 'T' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'Activator.CreateInstance<T>()'
// _ = new T();
Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "new T()").WithArguments("System.Activator.CreateInstance<T>()", "T", "T").WithLocation(7, 13)
);
}
[Fact]
public void SystemActivatorCreateInstance_03()
{
// 'System.Activator.CreateInstance<T>' will be changed to include 'allows ref struct' constraint,
// see https://github.com/dotnet/runtime/issues/65112.
var src = @"
public class Helper
{
public static T Test1<T>()
where T : new(), allows ref struct
{
return new T();
}
}
ref struct S
{
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
class Program
{
static void Main()
{
Print(Helper.Test1<S>());
System.Console.Write(' ');
Print(Helper.Test1<Program>());
}
static void Print<T>(T value) where T : allows ref struct
{
System.Console.Write(typeof(T));
System.Console.Write(' ');
System.Console.Write(value == null);
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "S False Program True" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper.Test1<T>()",
@"
{
// Code size 6 (0x6)
.maxstack 1
IL_0000: call ""T System.Activator.CreateInstance<T>()""
IL_0005: ret
}
");
}
[Fact]
public void Field()
{
var src = @"
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
class C<T>
where T : allows ref struct
{
public T P1;
public S P2;
}
ref struct S1<T>
where T : allows ref struct
{
public static T P3;
public static S P4;
}
ref struct S2<T>
where T : allows ref struct
{
public T P5;
public S P6;
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,12): error CS8345: Field or auto-implemented property cannot be of type 'T' unless it is an instance member of a ref struct.
// public T P1;
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "T").WithArguments("T").WithLocation(7, 12),
// (8,12): error CS8345: Field or auto-implemented property cannot be of type 'S' unless it is an instance member of a ref struct.
// public S P2;
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "S").WithArguments("S").WithLocation(8, 12),
// (14,19): error CS8345: Field or auto-implemented property cannot be of type 'T' unless it is an instance member of a ref struct.
// public static T P3;
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "T").WithArguments("T").WithLocation(14, 19),
// (15,19): error CS8345: Field or auto-implemented property cannot be of type 'S' unless it is an instance member of a ref struct.
// public static S P4;
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "S").WithArguments("S").WithLocation(15, 19)
);
}
[Fact]
public void AutoProperty()
{
var src = @"
class C<T>
where T : allows ref struct
{
public T P1 {get; set;}
public S P2 {get; set;}
}
ref struct S1<T>
where T : allows ref struct
{
public static T P3 {get; set;}
public static S P4 {get; set;}
}
ref struct S2<T>
where T : allows ref struct
{
public T P5 {get; set;}
public S P6 {get; set;}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (5,12): error CS8345: Field or auto-implemented property cannot be of type 'T' unless it is an instance member of a ref struct.
// public T P1 {get; set;}
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "T").WithArguments("T").WithLocation(5, 12),
// (6,12): error CS8345: Field or auto-implemented property cannot be of type 'S' unless it is an instance member of a ref struct.
// public S P2 {get; set;}
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "S").WithArguments("S").WithLocation(6, 12),
// (12,19): error CS8345: Field or auto-implemented property cannot be of type 'T' unless it is an instance member of a ref struct.
// public static T P3 {get; set;}
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "T").WithArguments("T").WithLocation(12, 19),
// (13,19): error CS8345: Field or auto-implemented property cannot be of type 'S' unless it is an instance member of a ref struct.
// public static S P4 {get; set;}
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "S").WithArguments("S").WithLocation(13, 19)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/73556")]
public void InlineArrayElement_01()
{
var src1 = @"
[System.Runtime.CompilerServices.InlineArray(10)]
ref struct S1<T>
where T : allows ref struct
{
T _f;
}
[System.Runtime.CompilerServices.InlineArray(10)]
ref struct S2
{
S _f;
}
ref struct S
{
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
CompileAndVerify(
comp1,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics(
// (3,12): warning CS9184: 'Inline arrays' language feature is not supported for an inline array type that is not valid as a type argument, or has element type that is not valid as a type argument.
// ref struct S1<T>
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "S1").WithLocation(3, 12),
// (10,12): warning CS9184: 'Inline arrays' language feature is not supported for an inline array type that is not valid as a type argument, or has element type that is not valid as a type argument.
// ref struct S2
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "S2").WithLocation(10, 12)
);
var src2 = @"
[System.Runtime.CompilerServices.InlineArray(10)]
struct S2<T2>
where T2 : allows ref struct
{
T2 _f;
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyDiagnostics(
// (6,5): error CS8345: Field or auto-implemented property cannot be of type 'T2' unless it is an instance member of a ref struct.
// T2 _f;
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "T2").WithArguments("T2").WithLocation(6, 5)
);
}
[Fact]
public void InlineArrayElement_02()
{
var src = @"
[System.Runtime.CompilerServices.InlineArray(2)]
ref struct S1<T>
where T : allows ref struct
{
T _f;
}
class C
{
static void Main()
{
var x = new S1<int>();
x[0] = 123;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (3,12): warning CS9184: 'Inline arrays' language feature is not supported for an inline array type that is not valid as a type argument, or has element type that is not valid as a type argument.
// ref struct S1<T>
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "S1").WithLocation(3, 12),
// (14,9): error CS0306: The type 'S1<int>' may not be used as a type argument
// x[0] = 123;
Diagnostic(ErrorCode.ERR_BadTypeArgument, "x[0]").WithArguments("S1<int>").WithLocation(14, 9)
);
}
[Fact]
public void InlineArrayElement_03()
{
var src = @"
[System.Runtime.CompilerServices.InlineArray(2)]
ref struct S1<T>
where T : allows ref struct
{
T _f;
}
class C
{
static void Main()
{
var x = new S1<int>();
x[0] = 123;
}
}
namespace System.Runtime.CompilerServices
{
public class Unsafe
{
public static ref TTo As<TFrom, TTo>(ref TFrom input) where TFrom : allows ref struct => throw null;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.DebugExe);
comp.VerifyEmitDiagnostics(
// (3,12): warning CS9184: 'Inline arrays' language feature is not supported for an inline array type that is not valid as a type argument, or has element type that is not valid as a type argument.
// ref struct S1<T>
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "S1").WithLocation(3, 12),
// (14,9): error CS0306: The type 'S1<int>' may not be used as a type argument
// x[0] = 123;
Diagnostic(ErrorCode.ERR_BadTypeArgument, "x[0]").WithArguments("S1<int>").WithLocation(14, 9)
);
}
[Fact]
public void AsyncParameter()
{
var src = @"
#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously.
public class Helper
{
static async void Test1<T>(T x)
where T : allows ref struct
{
}
static async void Test2(S y)
{
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (6,34): error CS4012: Parameters of type 'T' cannot be declared in async methods or async lambda expressions.
// static async void Test1<T>(T x)
Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "x").WithArguments("T").WithLocation(6, 34),
// (11,31): error CS4012: Parameters of type 'S' cannot be declared in async methods or async lambda expressions.
// static async void Test2(S y)
Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "y").WithArguments("S").WithLocation(11, 31)
);
}
[Fact]
public void MissingScopedInOverride_01()
{
var src = @"
abstract class Base
{
protected abstract T Test1<T>(scoped T x)
where T : allows ref struct;
protected abstract S Test2(scoped S y);
}
abstract class Derived1 : Base
{
protected abstract override T Test1<T>(T x);
protected abstract override S Test2(S y);
}
abstract class Derived2 : Base
{
protected abstract override T Test1<T>(scoped T x);
protected abstract override S Test2(scoped S y);
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (12,35): error CS8987: The 'scoped' modifier of parameter 'x' doesn't match overridden or implemented member.
// protected abstract override T Test1<T>(T x);
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("x").WithLocation(12, 35),
// (14,35): error CS8987: The 'scoped' modifier of parameter 'y' doesn't match overridden or implemented member.
// protected abstract override S Test2(S y);
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("y").WithLocation(14, 35)
);
}
[Fact]
public void MissingScopedInOverride_02()
{
var src = @"
abstract class Base
{
protected abstract void Test1<T>(scoped T x, out T z)
where T : allows ref struct;
protected abstract void Test2(scoped S y, out S z);
}
abstract class Derived1 : Base
{
protected abstract override void Test1<T>(T x, out T z);
protected abstract override void Test2(S y, out S z);
}
abstract class Derived2 : Base
{
protected abstract override void Test1<T>(scoped T x, out T z);
protected abstract override void Test2(scoped S y, out S z);
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (12,38): error CS8987: The 'scoped' modifier of parameter 'x' doesn't match overridden or implemented member.
// protected abstract override void Test1<T>(T x, out T z);
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("x").WithLocation(12, 38),
// (14,38): error CS8987: The 'scoped' modifier of parameter 'y' doesn't match overridden or implemented member.
// protected abstract override void Test2(S y, out S z);
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("y").WithLocation(14, 38)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/73555")]
public void MissingScopedInOverride_03()
{
var src = @"
abstract class Base
{
protected abstract T Test1<T>(out T x)
where T : allows ref struct;
protected abstract S Test2(out S y);
protected abstract T Test3<T>(scoped out T x)
where T : allows ref struct;
protected abstract S Test4(scoped out S y);
}
abstract class Derived1 : Base
{
protected abstract override T Test1<T>(out T x);
protected abstract override S Test2(out S y);
protected abstract override T Test3<T>(out T x);
protected abstract override S Test4(out S y);
}
abstract class Derived2 : Base
{
protected abstract override T Test1<T>(scoped out T x);
protected abstract override S Test2(scoped out S y);
protected abstract override T Test3<T>(scoped out T x);
protected abstract override S Test4(scoped out S y);
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/73554")]
public void MissingScopedInPartial_01()
{
var src = @"
partial class C1
{
private partial void Test1<T>(scoped T x, out T z) where T : allows ref struct;
private partial void Test2(scoped S y, out S z);
}
partial class C1
{
private partial void Test1<T>(T x, out T z) where T : allows ref struct => throw null;
private partial void Test2(S y, out S z) => throw null;
}
partial class C2
{
private partial void Test3<T>(scoped T x, out T z) where T : allows ref struct;
private partial void Test4(scoped S y, out S z);
}
partial class C2
{
private partial void Test3<T>(scoped T x, out T z) where T : allows ref struct => throw null;
private partial void Test4(scoped S y, out S z) => throw null;
}
partial class C3
{
private partial void Test5<T>(T x, out T z) where T : allows ref struct;
private partial void Test6(S y, out S z);
}
partial class C3
{
private partial void Test5<T>(scoped T x, out T z) where T : allows ref struct => throw null;
private partial void Test6(scoped S y, out S z) => throw null;
}
partial class C4
{
private partial void Test7<T>(T x, out T z) where T : allows ref struct;
private partial void Test8(S y, out S z);
}
partial class C4
{
private partial void Test7<T>(T x, out T z) where T : allows ref struct => throw null;
private partial void Test8(S y, out S z) => throw null;
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (11,26): error CS8988: The 'scoped' modifier of parameter 'x' doesn't match partial definition.
// private partial void Test1<T>(T x, out T z) where T : allows ref struct => throw null;
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test1").WithArguments("x").WithLocation(11, 26),
// (13,26): error CS8988: The 'scoped' modifier of parameter 'y' doesn't match partial definition.
// private partial void Test2(S y, out S z) => throw null;
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test2").WithArguments("y").WithLocation(13, 26),
// (40,26): error CS8988: The 'scoped' modifier of parameter 'x' doesn't match partial definition.
// private partial void Test5<T>(scoped T x, out T z) where T : allows ref struct => throw null;
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test5").WithArguments("x").WithLocation(40, 26),
// (42,26): error CS8988: The 'scoped' modifier of parameter 'y' doesn't match partial definition.
// private partial void Test6(scoped S y, out S z) => throw null;
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test6").WithArguments("y").WithLocation(42, 26)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/73554")]
public void MissingScopedInDelegateTarget_01()
{
var src = @"
class Program
{
static void Test<T>() where T : allows ref struct
{
DTest1<T> d01 = C1.Test1;
DTest2 d02 = C1.Test2;
DTest3<T> d03 = C1.Test1;
DTest4 d04 = C1.Test2;
DTest1<T> d11 = C2.Test1;
DTest2 d12 = C2.Test2;
DTest3<T> d13 = C2.Test1;
DTest4 d14 = C2.Test2;
}
}
delegate void DTest1<T>(scoped T x, out T z) where T : allows ref struct;
delegate void DTest2(scoped S y, out S z);
delegate void DTest3<T>(T x, out T z) where T : allows ref struct;
delegate void DTest4(S y, out S z);
class C1
{
public static void Test1<T>(T x, out T z) where T : allows ref struct => throw null;
public static void Test2(S y, out S z) => throw null;
}
class C2
{
public static void Test1<T>(scoped T x, out T z) where T : allows ref struct => throw null;
public static void Test2(scoped S y, out S z) => throw null;
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (6,25): error CS8986: The 'scoped' modifier of parameter 'x' doesn't match target 'DTest1<T>'.
// DTest1<T> d01 = C1.Test1;
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "C1.Test1").WithArguments("x", "DTest1<T>").WithLocation(6, 25),
// (7,22): error CS8986: The 'scoped' modifier of parameter 'y' doesn't match target 'DTest2'.
// DTest2 d02 = C1.Test2;
Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "C1.Test2").WithArguments("y", "DTest2").WithLocation(7, 22)
);
}
[ConditionalFact(typeof(CoreClrOnly))]
[WorkItem("https://github.com/dotnet/roslyn/issues/73553")]
public void RefFieldTypeAllowsRefLike()
{
// ref struct R2<T> where T : allows ref struct
// {
// public ref T F;
// }
var sourceA =
@"
.class public sealed R2`1<byreflike T> extends [mscorlib]System.ValueType
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (01 00 00 00)
.field public !T& F
}
";
var refA = CompileIL(sourceA);
var sourceB =
@"class Program
{
static void F<T >(ref T r1) where T : allows ref struct
{
var r2 = new R2<T>();
r2.F = ref r1;
}
}";
var comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (6,12): error CS0570: 'R2.F' is not supported by the language
// r2.F = ref r1;
Diagnostic(ErrorCode.ERR_BindToBogus, "F").WithArguments("R2<T>.F").WithLocation(6, 12)
);
}
[Fact]
public void RestrictedTypesInRecords()
{
var src = @"
record C<T>(
T P1
) where T : allows ref struct;
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (3,5): error CS8345: Field or auto-implemented property cannot be of type 'T' unless it is an instance member of a ref struct.
// T P1
Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "T").WithArguments("T").WithLocation(3, 5)
);
}
[Theory]
[CombinatorialData]
public void AnonymousDelegateType_01_ActionDisallowsRefStruct(bool s2IsRefStruct)
{
var src = @"
class C
{
static void Main()
{
Test1(new S1());
}
static void Test1<T>(T x) where T : allows ref struct
{
var d = Helper<T>.Test1;
System.Console.Write(d.GetType());
System.Console.Write("" "");
d(x, new S2());
}
}
class Helper<T> where T : allows ref struct
{
public static void Test1(T x, S2 y)
{
System.Console.Write(""Test1"");
System.Console.Write("" "");
System.Console.Write(typeof(T));
}
}
ref struct S1 {}
" + (s2IsRefStruct ? "ref " : "") + @"struct S2 {}
namespace System
{
public delegate void Action<T1, T2>(T1 x, T2 y);
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"<>A`2[S1,S2] Test1 S1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped,
symbolValidator: (m) =>
{
foreach (var tp in m.ContainingAssembly.GetTypeByMetadataName("<>A`2").TypeParameters)
{
Assert.True(tp.AllowsRefLikeType);
}
}
).VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void AnonymousDelegateType_02_FuncDisallowsRefStruct(bool s2IsRefStruct)
{
var src = @"
class C
{
static void Main()
{
Test1(new S1());
}
static void Test1<T>(T x) where T : allows ref struct
{
var d = Helper<T>.Test1;
System.Console.Write(d.GetType());
System.Console.Write("" "");
d(x);
}
}
class Helper<T> where T : allows ref struct
{
public static S2 Test1(T x)
{
System.Console.Write(""Test1"");
System.Console.Write("" "");
System.Console.Write(typeof(T));
return default;
}
}
ref struct S1 {}
" + (s2IsRefStruct ? "ref " : "") + @"struct S2 {}
namespace System
{
public delegate T2 Func<T1, T2>(T1 x);
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"<>F`2[S1,S2] Test1 S1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr && !s2IsRefStruct ? Verification.Passes : Verification.Skipped,
symbolValidator: (m) =>
{
foreach (var tp in m.ContainingAssembly.GetTypeByMetadataName("<>F`2").TypeParameters)
{
Assert.True(tp.AllowsRefLikeType);
}
}
).VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void AnonymousDelegateType_03_ActionAllowsRefStruct(bool s2IsRefStruct)
{
var src = @"
class C
{
static void Main()
{
Test1(new S1());
}
static void Test1<T>(T x) where T : allows ref struct
{
var d = Helper<T>.Test1;
System.Console.Write(d.GetType());
System.Console.Write("" "");
d(x, new S2());
}
}
class Helper<T> where T : allows ref struct
{
public static void Test1(T x, S2 y)
{
System.Console.Write(""Test1"");
System.Console.Write("" "");
System.Console.Write(typeof(T));
}
}
ref struct S1 {}
" + (s2IsRefStruct ? "ref " : "") + @"struct S2 {}
namespace System
{
public delegate void Action<T1, T2>(T1 x, T2 y) where T1 : allows ref struct where T2 : allows ref struct;
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"System.Action`2[S1,S2] Test1 S1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void AnonymousDelegateType_04_FuncAllowsRefStruct(bool s2IsRefStruct)
{
var src = @"
class C
{
static void Main()
{
Test1(new S1());
}
static void Test1<T>(T x) where T : allows ref struct
{
var d = Helper<T>.Test1;
System.Console.Write(d.GetType());
System.Console.Write("" "");
d(x);
}
}
class Helper<T> where T : allows ref struct
{
public static S2 Test1(T x)
{
System.Console.Write(""Test1"");
System.Console.Write("" "");
System.Console.Write(typeof(T));
return default;
}
}
ref struct S1 {}
" + (s2IsRefStruct ? "ref " : "") + @"struct S2 {}
namespace System
{
public delegate T2 Func<T1, T2>(T1 x) where T1 : allows ref struct where T2 : allows ref struct;
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"System.Func`2[S1,S2] Test1 S1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr && !s2IsRefStruct ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void AnonymousDelegateType_05_PartiallyGenericAnonymousDelegate()
{
var src = @"
unsafe class C
{
static void Main()
{
Test1(new S1());
}
static void Test1<T>(T x) where T : allows ref struct
{
var d = Helper<T>.Test1;
System.Console.Write(d.GetType());
System.Console.Write("" "");
d(x, null);
}
}
unsafe class Helper<T> where T : allows ref struct
{
public static void Test1(T x, void* y)
{
System.Console.Write(""Test1"");
System.Console.Write("" "");
System.Console.Write(typeof(T));
}
}
ref struct S1 {}
namespace System
{
public delegate void Action<T1, T2>(T1 x, T2 y) where T1 : allows ref struct where T2 : allows ref struct;
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.UnsafeReleaseExe);
CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"<>f__AnonymousDelegate0`1[S1] Test1 S1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped,
symbolValidator: (m) =>
{
foreach (var tp in m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0`1").TypeParameters)
{
Assert.True(tp.AllowsRefLikeType);
}
}
).VerifyDiagnostics();
}
[Fact]
public void AnonymousDelegateType_06_PartiallyGenericAnonymousDelegate_CannotAllowRefStruct()
{
var src = @"
class C
{
static void Main()
{
Test1(new S1());
}
static void Test1<T>(T x)
{
var d = Helper<T>.Test1;
System.Console.Write(d.GetType());
System.Console.Write("" "");
d(x, new S2());
}
}
class Helper<T>
{
public static void Test1(T x, S2 y)
{
System.Console.Write(""Test1"");
System.Console.Write("" "");
System.Console.Write(typeof(T));
}
}
struct S1 {}
ref struct S2 {}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe);
CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"<>f__AnonymousDelegate0`1[S1] Test1 S1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped,
symbolValidator: (m) =>
{
foreach (var tp in m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0`1").TypeParameters)
{
Assert.False(tp.AllowsRefLikeType);
}
}
).VerifyDiagnostics();
}
[Fact]
public void AnonymousDelegateType_07_ActionDisallowsRefStruct_CannotAllowRefStruct()
{
var src = @"
class C
{
static void Main()
{
Test1(new S1());
}
static void Test1(S1 x)
{
var d = Helper.Test1;
System.Console.Write(d.GetType());
System.Console.Write("" "");
d(x, new S2());
}
}
class Helper
{
public static void Test1(S1 x, S2 y)
{
System.Console.Write(""Test1"");
}
}
ref struct S1 {}
ref struct S2 {}
namespace System
{
public delegate void Action<T1, T2>(T1 x, T2 y);
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"<>f__AnonymousDelegate0 Test1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped
).VerifyDiagnostics();
}
[Fact]
public void AnonymousDelegateType_08_FuncDisallowsRefStruct_CannotAllowRefStruct()
{
var src = @"
class C
{
static void Main()
{
Test1(new S1());
}
static void Test1(S1 x)
{
var d = Helper.Test1;
System.Console.Write(d.GetType());
System.Console.Write("" "");
d(x);
}
}
class Helper
{
public static S2 Test1(S1 x)
{
System.Console.Write(""Test1"");
return default;
}
}
ref struct S1 {}
ref struct S2 {}
namespace System
{
public delegate T2 Func<T1, T2>(T1 x);
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? @"<>f__AnonymousDelegate0 Test1" : null,
verify: Verification.Skipped
).VerifyDiagnostics();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_09_ParamsArray()
{
var source = """
var d = M;
System.Console.WriteLine(d.GetType());
void M(params int[] arr) { }
""";
var verifier = CompileAndVerify(source,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int32]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(params T1[] arg)", m.ToTestDisplayString());
Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_10_ParamsArray()
{
var source = """
var d = M;
System.Console.WriteLine(d.GetType());
void M(string s, params int[] arr) { }
""";
var verifier = CompileAndVerify(source,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`2[System.String,System.Int32]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1, T2>.Invoke(T1 arg1, params T2[] arg2)", m.ToTestDisplayString());
Assert.Equal([true, false], m.ContainingType.TypeParameters.Select(t => t.AllowsRefLikeType));
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_11_ParamsArray()
{
var source = """
unsafe
{
var d = M;
System.Console.WriteLine(d.GetType());
void M(int* p, params int[] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, params System.Int32[] arg2)", m.ToTestDisplayString());
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_12_ParamsArray()
{
var source = """
M<string>();
unsafe static void M<T>()
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T>
{
public static void M(int* p, T x, params int[] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, T1 arg2, params System.Int32[] arg3)", m.ToTestDisplayString());
Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_13_ParamsArray()
{
var source = """
M<string>();
unsafe static void M<T>() where T : allows ref struct
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T> where T : allows ref struct
{
public static void M(int* p, T x, params int[] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, T1 arg2, params System.Int32[] arg3)", m.ToTestDisplayString());
Assert.True(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_14_ParamsArray()
{
var source = """
M<string>();
unsafe static void M<T>()
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T>
{
public static void M(int* p, T x, params T[] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, T1 arg2, params T1[] arg3)", m.ToTestDisplayString());
Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_15_ParamsArray()
{
var source = """
M<string>();
unsafe static void M<T>()
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T>
{
public static void M(int* p, params T[] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, params T1[] arg2)", m.ToTestDisplayString());
Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_16_ParamsArray()
{
var source = """
#nullable enable
M<string>();
unsafe static void M<T>()
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T>
{
public static void M(int* p, params T?[] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, params T1?[] arg2)", m.ToTestDisplayString());
Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_17_ParamsArray()
{
var source = """
M<short>();
unsafe static void M<T>() where T : struct
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T> where T : struct
{
public static void M(int* p, params T?[] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int16]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, params T1?[] arg2)", m.ToTestDisplayString());
Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_18_ParamsArray()
{
var source = """
M<short>();
unsafe static void M<T>()
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T>
{
public static void M(int* p, params T[][] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int16]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, params T1[][] arg2)", m.ToTestDisplayString());
Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_19_ParamsCollection()
{
var source = """
var d = M;
System.Console.WriteLine(d.GetType());
void M(params System.Collections.Generic.IEnumerable<int> e) { }
""";
var verifier = CompileAndVerify(source,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable<System.Int32> arg)", m.ToTestDisplayString());
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_20()
{
var source = """
M<short>();
unsafe static void M<T>()
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T>
{
public static void M(int* p, T[] arr) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int16]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, T1[] arg2)", m.ToTestDisplayString());
Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_21()
{
var source = """
M<short>();
unsafe static void M<T>() where T : allows ref struct
{
var d = C<T>.M;
System.Console.WriteLine(d.GetType());
}
unsafe static class C<T> where T : allows ref struct
{
public static void M(int* p, T t) { }
}
""";
var verifier = CompileAndVerify(source,
options: TestOptions.UnsafeReleaseExe,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int16]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>f__AnonymousDelegate0.Invoke");
AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(System.Int32* arg1, T1 arg2)", m.ToTestDisplayString());
Assert.True(m.ContainingType.TypeParameters.Single().AllowsRefLikeType);
}
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")]
public void AnonymousDelegateType_22()
{
var source = """
var d = M;
System.Console.WriteLine(d.GetType());
void M(ref short p, int[] arr) { }
""";
var verifier = CompileAndVerify(source,
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics,
symbolValidator: validate,
verify: Verification.FailsPEVerify,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>A{00000001}`2[System.Int16,System.Int32[]]" : null);
verifier.VerifyDiagnostics();
static void validate(ModuleSymbol module)
{
var m = module.GlobalNamespace.GetMember<MethodSymbol>("<>A{00000001}.Invoke");
AssertEx.Equal("void <>A{00000001}<T1, T2>.Invoke(ref T1 arg1, T2 arg2)", m.ToTestDisplayString());
Assert.Equal([true, true], m.ContainingType.TypeParameters.Select(t => t.AllowsRefLikeType));
}
}
[Fact]
public void ExpressionTree_01()
{
var src = @"
public class Helper
{
static void Test1<T>()
where T : allows ref struct
{
System.Linq.Expressions.Expression<D1<T>> e1 = (x) => System.Console.WriteLine();
}
static void Test2()
{
System.Linq.Expressions.Expression<D2> e2 = (y) => System.Console.WriteLine();
}
delegate void D1<T>(T x) where T : allows ref struct;
delegate void D2(S x);
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,57): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'T'.
// System.Linq.Expressions.Expression<D1<T>> e1 = (x) => System.Console.WriteLine();
Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "x").WithArguments("T").WithLocation(7, 57),
// (12,54): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'S'.
// System.Linq.Expressions.Expression<D2> e2 = (y) => System.Console.WriteLine();
Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "y").WithArguments("S").WithLocation(12, 54)
);
}
[Fact]
public void ExpressionTree_02()
{
var src = @"
public class Helper1<T>
where T : allows ref struct
{
static void Test1()
{
System.Linq.Expressions.Expression<System.Action> e1 = () => M1();
}
static T M1() => throw null;
}
public class Helper2
{
static void Test2()
{
System.Linq.Expressions.Expression<System.Action> e2 = () => M2();
}
static S M2() => throw null;
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,70): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'T'.
// System.Linq.Expressions.Expression<System.Action> e1 = () => M1();
Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "M1()").WithArguments("T").WithLocation(7, 70),
// (17,70): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'S'.
// System.Linq.Expressions.Expression<System.Action> e2 = () => M2();
Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "M2()").WithArguments("S").WithLocation(17, 70)
);
}
[Fact]
public void InArrayType_01()
{
var src = @"
public class Helper1<T>
where T : allows ref struct
{
static T[] Test1()
=> throw null;
}
public class Helper2
{
static S[] Test2()
=> throw null;
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (5,12): error CS0611: Array elements cannot be of type 'T'
// static T[] Test1()
Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "T").WithArguments("T").WithLocation(5, 12),
// (11,12): error CS0611: Array elements cannot be of type 'S'
// static S[] Test2()
Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "S").WithArguments("S").WithLocation(11, 12)
);
}
[Fact]
public void InArrayType_02()
{
var src = @"
public class Helper1<T>
where T : allows ref struct
{
static void Test1()
{
_ = new T[] {};
}
}
public class Helper2
{
static void Test2()
{
_ = new S[] {};
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,17): error CS0611: Array elements cannot be of type 'T'
// _ = new T[] {};
Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "T").WithArguments("T").WithLocation(7, 17),
// (15,17): error CS0611: Array elements cannot be of type 'S'
// _ = new S[] {};
Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "S").WithArguments("S").WithLocation(15, 17)
);
}
[Fact]
public void InArrayType_03()
{
var src = @"
public class Helper1<T>
where T : allows ref struct
{
static void Test1(T x)
{
_ = new [] {x};
}
}
public class Helper2
{
static void Test2(S y)
{
_ = new [] {y};
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0611: Array elements cannot be of type 'T'
// _ = new [] {x};
Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "new [] {x}").WithArguments("T").WithLocation(7, 13),
// (15,13): error CS0611: Array elements cannot be of type 'S'
// _ = new [] {y};
Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "new [] {y}").WithArguments("S").WithLocation(15, 13)
);
}
[Fact]
public void DelegateReceiver_01()
{
var src = @"
class Helper1<T>
where T : I1, allows ref struct
{
static System.Action Test1(T x)
=> x.M;
}
class Helper2
{
static System.Action Test2(S y)
=> y.M;
}
ref struct S
{
public void M(){}
}
interface I1
{
void M();
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (6,14): error CS0123: No overload for 'M' matches delegate 'Action'
// => x.M;
Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Action").WithLocation(6, 14),
// (12,14): error CS0123: No overload for 'M' matches delegate 'Action'
// => y.M;
Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Action").WithLocation(12, 14)
);
}
[Fact]
public void DelegateReceiver_02()
{
var src = @"
class Helper1<T>
where T : I1, allows ref struct
{
static void Test1(T x)
{
var d1 = x.M;
}
}
class Helper2
{
static void Test2(S y)
{
var d2 = y.M;
}
}
ref struct S
{
public void M(){}
}
interface I1
{
void M();
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,20): error CS0123: No overload for 'M' matches delegate 'Action'
// var d1 = x.M;
Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Action").WithLocation(7, 20),
// (15,20): error CS0123: No overload for 'M' matches delegate 'Action'
// var d2 = y.M;
Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Action").WithLocation(15, 20)
);
}
[Fact]
public void DelegateReceiver_03()
{
var src = @"
class Helper1<T>
where T : I1, allows ref struct
{
static void Test1(T x)
{
var d1 = x.M;
}
}
class Helper2
{
static void Test2(S y)
{
var d2 = y.M;
}
}
ref struct S
{
public void M(ref int x){}
}
interface I1
{
void M(ref int x);
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,20): error CS0123: No overload for 'M' matches delegate '<anonymous delegate>'
// var d1 = x.M;
Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "<anonymous delegate>").WithLocation(7, 20),
// (15,20): error CS0123: No overload for 'M' matches delegate '<anonymous delegate>'
// var d2 = y.M;
Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "<anonymous delegate>").WithLocation(15, 20)
);
}
[Fact]
public void ConditionalAccess_01()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
static void Test1(Helper1<T> h1)
{
var x = h1?.M1();
}
T M1() => default;
}
class Helper2
{
static void Test2(Helper2 h2)
{
var x = h2?.M2();
}
S M2() => default;
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,20): error CS8978: 'T' cannot be made nullable.
// var x = h1?.M1();
Diagnostic(ErrorCode.ERR_CannotBeMadeNullable, ".M1()").WithArguments("T").WithLocation(7, 20),
// (17,20): error CS8978: 'S' cannot be made nullable.
// var x = h2?.M2();
Diagnostic(ErrorCode.ERR_CannotBeMadeNullable, ".M2()").WithArguments("S").WithLocation(17, 20)
);
}
[Fact]
public void ConditionalAccess_02()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
public static void Test1(Helper1<T> h1)
{
h1?.M1();
}
T M1()
{
System.Console.Write(""M1"");
return default;
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(null);
System.Console.Write(""_"");
Helper1<S>.Test1(new Helper1<S>());
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "_M1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(Helper1<T>)",
@"
{
// Code size 11 (0xb)
.maxstack 1
IL_0000: ldarg.0
IL_0001: brfalse.s IL_000a
IL_0003: ldarg.0
IL_0004: call ""T Helper1<T>.M1()""
IL_0009: pop
IL_000a: ret
}
");
}
[Fact]
public void ConditionalAccess_03()
{
var src = @"
class Helper1<T>
where T : I1, allows ref struct
{
public static void Test1(T h1)
{
System.Console.Write(h1?.M());
}
}
interface I1
{
int M();
}
ref struct S : I1
{
public int M() => 123;
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 48 (0x30)
.maxstack 1
.locals init (int? V_0)
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_0013
IL_0008: ldloca.s V_0
IL_000a: initobj ""int?""
IL_0010: ldloc.0
IL_0011: br.s IL_0025
IL_0013: ldarga.s V_0
IL_0015: constrained. ""T""
IL_001b: callvirt ""int I1.M()""
IL_0020: newobj ""int?..ctor(int)""
IL_0025: box ""int?""
IL_002a: call ""void System.Console.Write(object)""
IL_002f: ret
}
");
}
[Fact]
public void ConditionalAccess_04()
{
var src = @"
class Helper1<T>
where T : I1, allows ref struct
{
public static void Test1(T h1)
{
System.Console.Write(h1?.M());
}
}
interface I1
{
dynamic M();
}
ref struct S : I1
{
public dynamic M() => 123;
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 129 (0x81)
.maxstack 9
.locals init (T V_0,
T V_1)
IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic>> Helper1<T>.<>o__0.<>p__0""
IL_0005: brtrue.s IL_0046
IL_0007: ldc.i4 0x100
IL_000c: ldstr ""Write""
IL_0011: ldnull
IL_0012: ldtoken ""Helper1<T>""
IL_0017: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_001c: ldc.i4.2
IL_001d: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo""
IL_0022: dup
IL_0023: ldc.i4.0
IL_0024: ldc.i4.s 33
IL_0026: ldnull
IL_0027: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_002c: stelem.ref
IL_002d: dup
IL_002e: ldc.i4.1
IL_002f: ldc.i4.0
IL_0030: ldnull
IL_0031: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_0036: stelem.ref
IL_0037: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_003c: call ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic>> Helper1<T>.<>o__0.<>p__0""
IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic>> Helper1<T>.<>o__0.<>p__0""
IL_004b: ldfld ""System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic>>.Target""
IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic>> Helper1<T>.<>o__0.<>p__0""
IL_0055: ldtoken ""System.Console""
IL_005a: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_005f: ldarg.0
IL_0060: stloc.0
IL_0061: ldloc.0
IL_0062: box ""T""
IL_0067: brtrue.s IL_006c
IL_0069: ldnull
IL_006a: br.s IL_007b
IL_006c: ldloc.0
IL_006d: stloc.1
IL_006e: ldloca.s V_1
IL_0070: constrained. ""T""
IL_0076: callvirt ""dynamic I1.M()""
IL_007b: callvirt ""void System.Action<System.Runtime.CompilerServices.CallSite, System.Type, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Type, dynamic)""
IL_0080: ret
}
");
}
[Fact]
public void ConditionalAccess_05()
{
var src = @"
class Helper1<T>
where T : I1<T>, allows ref struct
{
void Test(out T y)
{
T x = T.Create(stackalloc int[2]);
if (x?.M(out y) == true)
{
return;
}
y = default;
}
}
interface I1<S> where S : I1<S>, allows ref struct
{
bool M(out S x);
static abstract S Create(System.Span<int> x);
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (8,15): error CS8350: This combination of arguments to 'I1<T>.M(out T)' is disallowed because it may expose variables referenced by parameter 'this' outside of their declaration scope
// if (x?.M(out y) == true)
Diagnostic(ErrorCode.ERR_CallArgMixing, ".M(out y)").WithArguments("I1<T>.M(out T)", "this").WithLocation(8, 15)
);
}
[Fact]
public void ConditionalAccess_06()
{
var src = @"
class Helper1<T>
where T : I1<T>, allows ref struct
{
void Test(T y)
{
T x = T.Create(stackalloc int[2]);
if (x?.M(y) == true)
{
return;
}
}
}
interface I1<S> where S : I1<S>, allows ref struct
{
bool M(S x);
static abstract S Create(System.Span<int> x);
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void DynamicAccess_01()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
static void Test1(dynamic h1)
{
h1.M1<T>();
}
}
class Helper2
{
static void Test2(dynamic h2)
{
h2.M2<S>();
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,15): error CS0306: The type 'T' may not be used as a type argument
// h1.M1<T>();
Diagnostic(ErrorCode.ERR_BadTypeArgument, "T").WithArguments("T").WithLocation(7, 15),
// (15,15): error CS0306: The type 'S' may not be used as a type argument
// h2.M2<S>();
Diagnostic(ErrorCode.ERR_BadTypeArgument, "S").WithArguments("S").WithLocation(15, 15)
);
}
[Fact]
public void DynamicAccess_02()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
static void Test1(dynamic h1, T x)
{
h1.M1(x);
}
}
class Helper2
{
static void Test2(dynamic h2, S y)
{
h2.M2(y);
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,15): error CS1978: Cannot use an expression of type 'T' as an argument to a dynamically dispatched operation.
// h1.M1(x);
Diagnostic(ErrorCode.ERR_BadDynamicMethodArg, "x").WithArguments("T").WithLocation(7, 15),
// (15,15): error CS1978: Cannot use an expression of type 'S' as an argument to a dynamically dispatched operation.
// h2.M2(y);
Diagnostic(ErrorCode.ERR_BadDynamicMethodArg, "y").WithArguments("S").WithLocation(15, 15)
);
}
[Fact]
public void DynamicAccess_03()
{
var src = @"
class Helper1<T>
where T : I1, allows ref struct
{
static void Test1(dynamic h1, T x)
{
x.M(h1);
}
}
class Helper2
{
static void Test2(dynamic h2, S y)
{
y.M(h2);
}
}
interface I1
{
void M(int x);
void M(long x);
}
ref struct S : I1
{
public void M(int x) => throw null;
public void M(long x) => throw null;
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,9): error CS9230: Cannot perform a dynamic invocation on an expression with type 'T'.
// x.M(h1);
Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, "x").WithArguments("T").WithLocation(7, 9),
// (15,9): error CS9230: Cannot perform a dynamic invocation on an expression with type 'S'.
// y.M(h2);
Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, "y").WithArguments("S").WithLocation(15, 9)
);
}
[Fact]
public void IsOperator_01()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(I1 h1)
{
if (h1 is T)
{
System.Console.Write(1);
}
else
{
System.Console.Write(2);
}
}
}
class Helper2
{
public static void Test2(I1 h2)
{
if (h2 is S)
{
System.Console.Write(3);
}
else
{
System.Console.Write(4);
}
}
}
ref struct S : I1
{
}
interface I1
{
}
struct S1 : I1 {}
class Program : I1
{
static void Main()
{
Helper1<S>.Test1(new S1());
Helper1<Program>.Test1(new Program());
Helper2.Test2(new S1());
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "214" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics(
// (22,13): warning CS0184: The given expression is never of the provided ('S') type
// if (h2 is S)
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "h2 is S").WithArguments("S").WithLocation(22, 13)
);
// According to
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2074169181 and
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2075815858
// the following is a valid IL
verifier.VerifyIL("Helper1<T>.Test1(I1)",
@"
{
// Code size 22 (0x16)
.maxstack 1
IL_0000: ldarg.0
IL_0001: isinst ""T""
IL_0006: brfalse.s IL_000f
IL_0008: ldc.i4.1
IL_0009: call ""void System.Console.Write(int)""
IL_000e: ret
IL_000f: ldc.i4.2
IL_0010: call ""void System.Console.Write(int)""
IL_0015: ret
}
");
verifier.VerifyIL("Helper2.Test2(I1)",
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.4
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}
");
}
[Fact]
public void IsOperator_02()
{
var src1 = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(T h1)
{
if (h1 is I1)
{
}
}
}
interface I1
{
}
";
var comp = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0019: Operator 'is' cannot be applied to operands of type 'T' and 'I1'
// if (h1 is I1)
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h1 is I1").WithArguments("is", "T", "I1").WithLocation(7, 13)
);
var src2 = @"
class Helper2
{
public static void Test2(S h2)
{
if (h2 is I1)
{
System.Console.Write(3);
}
else
{
System.Console.Write(4);
}
}
}
ref struct S : I1
{
}
interface I1
{
}
class Program
{
static void Main()
{
Helper2.Test2(new S());
}
}
";
comp = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "4" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics(
// (6,13): warning CS0184: The given expression is never of the provided ('I1') type
// if (h2 is I1)
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "h2 is I1").WithArguments("I1").WithLocation(6, 13)
);
verifier.VerifyIL("Helper2.Test2(S)",
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.4
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}
");
}
[Fact]
public void IsOperator_03()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(T h1)
{
if (h1 is T)
{
System.Console.Write(1);
}
else
{
System.Console.Write(2);
}
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
Helper1<Program>.Test1(new Program());
Helper1<Program>.Test1(null);
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "112" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 22 (0x16)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000f
IL_0008: ldc.i4.1
IL_0009: call ""void System.Console.Write(int)""
IL_000e: ret
IL_000f: ldc.i4.2
IL_0010: call ""void System.Console.Write(int)""
IL_0015: ret
}
");
}
[Fact]
public void IsOperator_04()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
public static void Test1(T h1)
{
if (h1 is T)
{
System.Console.Write(1);
}
else
{
System.Console.Write(2);
}
}
}
class Helper2
{
public static void Test2(S h2)
{
if (h2 is S)
{
System.Console.Write(3);
}
else
{
System.Console.Write(4);
}
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
Helper1<int>.Test1(1);
Helper2.Test2(new S());
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "113" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics(
// (7,13): warning CS0183: The given expression is always of the provided ('T') type
// if (h1 is T)
Diagnostic(ErrorCode.WRN_IsAlwaysTrue, "h1 is T").WithArguments("T").WithLocation(7, 13),
// (22,13): warning CS0183: The given expression is always of the provided ('S') type
// if (h2 is S)
Diagnostic(ErrorCode.WRN_IsAlwaysTrue, "h2 is S").WithArguments("S").WithLocation(22, 13)
);
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.1
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}
");
verifier.VerifyIL("Helper2.Test2(S)",
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.3
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}
");
}
[Theory]
[CombinatorialData]
public void IsOperator_05(bool uHasClassConstraint, bool uHasInterfaceConstraint)
{
var uConstraint = GetUConstraint(uHasClassConstraint, uHasInterfaceConstraint);
var src1 = @"
class Helper1<T, U>
where T : allows ref struct" + uConstraint + @"
{
public static void Test1(T h1)
{
if (h1 is U)
{
}
}
}
interface I1
{
}
";
var comp = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0019: Operator 'is' cannot be applied to operands of type 'T' and 'U'
// if (h1 is U)
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h1 is U").WithArguments("is", "T", "U").WithLocation(7, 13)
);
var src2 = @"
class Helper2<U>" + uConstraint + @"
{
public static void Test2(S h2)
{
if (h2 is U)
{
System.Console.Write(3);
}
else
{
System.Console.Write(4);
}
}
}
ref struct S : I1
{
}
interface I1
{
}
class Program
{
static void Main()
{
Helper2<I1>.Test2(new S());
}
}
";
comp = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "4" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics(
// (6,13): warning CS0184: The given expression is never of the provided ('U') type
// if (h2 is U)
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "h2 is U").WithArguments("U").WithLocation(6, 13)
);
verifier.VerifyIL("Helper2<U>.Test2(S)",
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.4
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}
");
}
private static string GetUConstraint(bool uHasClassConstraint, bool uHasInterfaceConstraint)
{
if (!uHasClassConstraint && !uHasInterfaceConstraint)
{
return "";
}
return " where U :" +
(uHasClassConstraint ? " class" : "") +
((uHasClassConstraint && uHasInterfaceConstraint) ? "," : "") +
(uHasInterfaceConstraint ? " I1" : "");
}
[Fact]
public void IsOperator_06()
{
var src = @"
class Helper1<T, U>
where T : allows ref struct
where U : allows ref struct
{
public static void Test1(T h1)
{
if (h1 is U)
{
}
}
}
class Helper2<U>
where U : allows ref struct
{
public static void Test2(S h2)
{
if (h2 is U)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (8,13): error CS0019: Operator 'is' cannot be applied to operands of type 'T' and 'U'
// if (h1 is U)
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h1 is U").WithArguments("is", "T", "U").WithLocation(8, 13),
// (19,13): error CS0019: Operator 'is' cannot be applied to operands of type 'S' and 'U'
// if (h2 is U)
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h2 is U").WithArguments("is", "S", "U").WithLocation(19, 13)
);
}
[Theory]
[CombinatorialData]
public void IsOperator_07(bool uHasClassConstraint, bool uHasInterfaceConstraint)
{
var uConstraint = GetUConstraint(uHasClassConstraint, uHasInterfaceConstraint);
var src = @"
class Helper1<T, U>
where T : allows ref struct" + uConstraint + @"
{
public static void Test1(U h1)
{
if (h1 is T)
{
System.Console.Write(1);
}
else
{
System.Console.Write(2);
}
}
}
class Helper2<U>" + uConstraint + @"
{
public static void Test2(U h2)
{
if (h2 is S)
{
System.Console.Write(3);
}
else
{
System.Console.Write(4);
}
}
}
ref struct S : I1
{
}
interface I1
{
}
struct S1 : I1 {}
struct S2 {}
class Program : I1
{
static void Main()
{
Helper1<S, I1>.Test1(new Program());
Helper1<Program, I1>.Test1(new Program());
Helper1<I1, Program>.Test1(new Program());
Helper1<Program, Program>.Test1(new Program());" +
(uHasClassConstraint ? "" :
@"
Helper1<I1, S1>.Test1(new S1());") +
@"
Helper1<S1, I1>.Test1(new S1());
Helper1<I1, I1>.Test1(new Program());" +
(uHasClassConstraint ? "" :
@"
Helper1<S1, S1>.Test1(new S1());") +
(uHasClassConstraint || uHasInterfaceConstraint ? "" :
@"
Helper1<I1, S2>.Test1(new S2());") +
@"
Helper1<S2, I1>.Test1(new S1());" +
(uHasClassConstraint || uHasInterfaceConstraint ? "" :
@"
Helper1<S2, S2>.Test1(new S2());") +
@"
Helper2<I1>.Test2(new Program());
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ?
(uHasClassConstraint ?
"21111124" :
(uHasInterfaceConstraint ?
"2111111124" :
"211111112214")) :
null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics(
// (22,13): warning CS0184: The given expression is never of the provided ('S') type
// if (h2 is S)
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "h2 is S").WithArguments("S").WithLocation(22, 13)
);
// According to
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2074169181 and
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2075815858
// the following is a valid IL
verifier.VerifyIL("Helper1<T, U>.Test1(U)",
@"
{
// Code size 27 (0x1b)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""U""
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_0014
IL_000d: ldc.i4.1
IL_000e: call ""void System.Console.Write(int)""
IL_0013: ret
IL_0014: ldc.i4.2
IL_0015: call ""void System.Console.Write(int)""
IL_001a: ret
}
");
verifier.VerifyIL("Helper2<U>.Test2(U)",
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.4
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}
");
}
[Fact]
public void IsOperator_08()
{
var src = @"
class Helper2<U>
where U : allows ref struct
{
public static void Test2(U h2)
{
if (h2 is S)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0019: Operator 'is' cannot be applied to operands of type 'U' and 'S'
// if (h2 is S)
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h2 is S").WithArguments("is", "U", "S").WithLocation(7, 13)
);
}
[Fact]
public void IsOperator_09()
{
var src = @"
class Helper2
{
public static void Test2(S1 h2)
{
if (h2 is S2)
{
System.Console.Write(3);
}
else
{
System.Console.Write(4);
}
}
}
ref struct S1
{
}
ref struct S2
{
}
class Program
{
static void Main()
{
Helper2.Test2(new S1());
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "4" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics(
// (6,13): warning CS0184: The given expression is never of the provided ('S2') type
// if (h2 is S2)
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "h2 is S2").WithArguments("S2").WithLocation(6, 13)
);
verifier.VerifyIL("Helper2.Test2(S1)",
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.4
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}
");
}
[Fact]
public void IsOperator_10()
{
var src = @"
class Helper1<T, U>
where T : allows ref struct
where U : T, allows ref struct
{
public static void Test1(T h1)
{
if (h1 is U)
{
}
}
public static void Test2(U h2)
{
if (h2 is T)
{
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (8,13): error CS0019: Operator 'is' cannot be applied to operands of type 'T' and 'U'
// if (h1 is U)
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h1 is U").WithArguments("is", "T", "U").WithLocation(8, 13),
// (14,13): error CS0019: Operator 'is' cannot be applied to operands of type 'U' and 'T'
// if (h2 is T)
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h2 is T").WithArguments("is", "U", "T").WithLocation(14, 13)
);
}
[Fact]
public void IsOperator_11()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(S h1)
{
if (h1 is T)
{
System.Console.Write(1);
}
else
{
System.Console.Write(2);
}
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseDll);
comp.VerifyDiagnostics(
// (7,13): error CS0019: Operator 'is' cannot be applied to operands of type 'S' and 'T'
// if (h1 is T)
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h1 is T").WithArguments("is", "S", "T").WithLocation(7, 13)
);
}
[Fact]
public void IsOperator_12()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(S1 h1)
{
if (h1 is T)
{
System.Console.Write(1);
}
else
{
System.Console.Write(2);
}
}
}
class Helper2
{
public static void Test2(S1 h2)
{
if (h2 is S)
{
System.Console.Write(3);
}
else
{
System.Console.Write(4);
}
}
}
ref struct S
{
}
struct S1 {}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S1());
Helper1<S1>.Test1(new S1());
Helper1<Program>.Test1(new S1());
Helper2.Test2(new S1());
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "2124" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics(
// (22,13): warning CS0184: The given expression is never of the provided ('S') type
// if (h2 is S)
Diagnostic(ErrorCode.WRN_IsAlwaysFalse, "h2 is S").WithArguments("S").WithLocation(22, 13)
);
// According to
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2074169181 and
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2075815858
// the following is a valid IL
verifier.VerifyIL("Helper1<T>.Test1(S1)",
@"
{
// Code size 27 (0x1b)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""S1""
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_0014
IL_000d: ldc.i4.1
IL_000e: call ""void System.Console.Write(int)""
IL_0013: ret
IL_0014: ldc.i4.2
IL_0015: call ""void System.Console.Write(int)""
IL_001a: ret
}
");
verifier.VerifyIL("Helper2.Test2(S1)",
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.4
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}
");
}
[Fact]
public void IsPattern_01()
{
var src1 = @"
class Helper1<T>
where T : I1, allows ref struct
{
public static void Test1(I1 h1)
{
if (h1 is T t)
{
t.M();
}
else
{
System.Console.Write(2);
}
}
}
ref struct S : I1
{
public void M()
{
System.Console.Write(3);
}
}
interface I1
{
void M();
}
class Program : I1
{
static void Main()
{
Helper1<S>.Test1(new Program());
Helper1<Program>.Test1(new Program());
}
public void M()
{
System.Console.Write(1);
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "21" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
// According to
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2074169181 and
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2075815858
// the following is a valid IL
verifier.VerifyIL("Helper1<T>.Test1(I1)",
@"
{
// Code size 41 (0x29)
.maxstack 1
.locals init (T V_0) //t
IL_0000: ldarg.0
IL_0001: isinst ""T""
IL_0006: brfalse.s IL_0022
IL_0008: ldarg.0
IL_0009: isinst ""T""
IL_000e: unbox.any ""T""
IL_0013: stloc.0
IL_0014: ldloca.s V_0
IL_0016: constrained. ""T""
IL_001c: callvirt ""void I1.M()""
IL_0021: ret
IL_0022: ldc.i4.2
IL_0023: call ""void System.Console.Write(int)""
IL_0028: ret
}
");
var src2 = @"
class Helper2
{
static void Test2(I1 h2)
{
if (h2 is S s)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyDiagnostics(
// (6,19): error CS8121: An expression of type 'I1' cannot be handled by a pattern of type 'S'.
// if (h2 is S s)
Diagnostic(ErrorCode.ERR_PatternWrongType, "S").WithArguments("I1", "S").WithLocation(6, 19)
);
}
[Fact]
public void IsPattern_02()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
static void Test1(T h1)
{
if (h1 is I1 t)
{
}
}
}
class Helper2
{
static void Test2(S h2)
{
if (h2 is I1 s)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,19): error CS8121: An expression of type 'T' cannot be handled by a pattern of type 'I1'.
// if (h1 is I1 t)
Diagnostic(ErrorCode.ERR_PatternWrongType, "I1").WithArguments("T", "I1").WithLocation(7, 19),
// (17,19): error CS8121: An expression of type 'S' cannot be handled by a pattern of type 'I1'.
// if (h2 is I1 s)
Diagnostic(ErrorCode.ERR_PatternWrongType, "I1").WithArguments("S", "I1").WithLocation(17, 19)
);
}
[Fact]
public void IsPattern_03()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(T h1)
{
if (h1 is T t)
{
Program.M(t);
System.Console.Write(1);
}
else
{
System.Console.Write(2);
}
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
Helper1<Program>.Test1(new Program());
Helper1<Program>.Test1(null);
}
public static void M<T>(T t) where T : allows ref struct {}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "112" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 30 (0x1e)
.maxstack 1
.locals init (T V_0) //t
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_0017
IL_0008: ldarg.0
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: call ""void Program.M<T>(T)""
IL_0010: ldc.i4.1
IL_0011: call ""void System.Console.Write(int)""
IL_0016: ret
IL_0017: ldc.i4.2
IL_0018: call ""void System.Console.Write(int)""
IL_001d: ret
}
");
}
[Fact]
public void IsPattern_04()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
public static void Test1(T h1)
{
if (h1 is T t)
{
Program.M(t);
System.Console.Write(1);
}
else
{
System.Console.Write(2);
}
}
}
class Helper2
{
public static void Test2(S h2)
{
if (h2 is S s)
{
Program.M(s);
System.Console.Write(3);
}
else
{
System.Console.Write(4);
}
}
}
ref struct S
{
}
class Program
{
static void Main()
{
Helper1<S>.Test1(new S());
Helper1<int>.Test1(1);
Helper2.Test2(new S());
}
public static void M<T>(T t) where T : allows ref struct {}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "113" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).
VerifyDiagnostics();
verifier.VerifyIL("Helper1<T>.Test1(T)",
@"
{
// Code size 15 (0xf)
.maxstack 1
.locals init (T V_0) //t
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: call ""void Program.M<T>(T)""
IL_0008: ldc.i4.1
IL_0009: call ""void System.Console.Write(int)""
IL_000e: ret
}
");
verifier.VerifyIL("Helper2.Test2(S)",
@"
{
// Code size 15 (0xf)
.maxstack 1
.locals init (S V_0) //s
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: call ""void Program.M<S>(S)""
IL_0008: ldc.i4.3
IL_0009: call ""void System.Console.Write(int)""
IL_000e: ret
}
");
}
[Theory]
[CombinatorialData]
public void IsPattern_05(bool uHasClassConstraint, bool uHasInterfaceConstraint)
{
var uConstraint = GetUConstraint(uHasClassConstraint, uHasInterfaceConstraint);
var src = @"
class Helper1<T, U>
where T : allows ref struct" + uConstraint + @"
{
public static void Test1(T h1)
{
if (h1 is U u)
{
}
}
}
class Helper2<U>" + uConstraint + @"
{
public static void Test2(S h2)
{
if (h2 is U u)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,19): error CS8121: An expression of type 'T' cannot be handled by a pattern of type 'U'.
// if (h1 is U u)
Diagnostic(ErrorCode.ERR_PatternWrongType, "U").WithArguments("T", "U").WithLocation(7, 19),
// (17,19): error CS8121: An expression of type 'S' cannot be handled by a pattern of type 'U'.
// if (h2 is U u)
Diagnostic(ErrorCode.ERR_PatternWrongType, "U").WithArguments("S", "U").WithLocation(17, 19)
);
}
[Fact]
public void IsPattern_06()
{
var src = @"
class Helper1<T, U>
where T : allows ref struct
where U : allows ref struct
{
public static void Test1(T h1)
{
if (h1 is U u)
{
}
}
}
class Helper2<U>
where U : allows ref struct
{
public static void Test2(S h2)
{
if (h2 is U u)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (8,19): error CS8121: An expression of type 'T' cannot be handled by a pattern of type 'U'.
// if (h1 is U u)
Diagnostic(ErrorCode.ERR_PatternWrongType, "U").WithArguments("T", "U").WithLocation(8, 19),
// (19,19): error CS8121: An expression of type 'S' cannot be handled by a pattern of type 'U'.
// if (h2 is U u)
Diagnostic(ErrorCode.ERR_PatternWrongType, "U").WithArguments("S", "U").WithLocation(19, 19)
);
}
[Theory]
[CombinatorialData]
public void IsPattern_07(bool uHasClassConstraint, bool uHasInterfaceConstraint)
{
var uConstraint = GetUConstraint(uHasClassConstraint, uHasInterfaceConstraint);
var src1 = @"
class Helper1<T, U>
where T : I2, allows ref struct" + uConstraint + @"
{
public static void Test1(U h1)
{
if (h1 is T t)
{
t.M();
}
else
{
System.Console.Write(2);
}
}
}
ref struct S : I1, I2
{
public void M() => throw null;
}
interface I1 : I2
{
}
interface I2
{
void M();
}
struct S1 : I1
{
public void M() => System.Console.Write(5);
}
struct S2 : I2
{
public void M() => System.Console.Write(4);
}
class Program : I1, I2
{
static void Main()
{
Helper1<S, I1>.Test1(new Program());
Helper1<Program, I1>.Test1(new Program());
Helper1<I1, Program>.Test1(new Program());
Helper1<Program, Program>.Test1(new Program());" +
(uHasClassConstraint ? "" :
@"
Helper1<I1, S1>.Test1(new S1());") +
@"
Helper1<S1, I1>.Test1(new S1());
Helper1<I1, I1>.Test1(new Program());" +
(uHasClassConstraint ? "" :
@"
Helper1<S1, S1>.Test1(new S1());") +
(uHasClassConstraint || uHasInterfaceConstraint ? "" :
@"
Helper1<I1, S2>.Test1(new S2());") +
@"
Helper1<S2, I1>.Test1(new S1());" +
(uHasClassConstraint || uHasInterfaceConstraint ? "" :
@"
Helper1<S2, S2>.Test1(new S2());") +
@"
}
public void M() => System.Console.Write(3);
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ?
(uHasClassConstraint ?
"2333532" :
(uHasInterfaceConstraint ?
"233355352" :
"23335535224")) :
null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
// According to
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2074169181 and
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2075815858
// the following is a valid IL
verifier.VerifyIL("Helper1<T, U>.Test1(U)",
@"
{
// Code size 51 (0x33)
.maxstack 1
.locals init (T V_0) //t
IL_0000: ldarg.0
IL_0001: box ""U""
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_002c
IL_000d: ldarg.0
IL_000e: box ""U""
IL_0013: isinst ""T""
IL_0018: unbox.any ""T""
IL_001d: stloc.0
IL_001e: ldloca.s V_0
IL_0020: constrained. ""T""
IL_0026: callvirt ""void I2.M()""
IL_002b: ret
IL_002c: ldc.i4.2
IL_002d: call ""void System.Console.Write(int)""
IL_0032: ret
}
");
var src2 = @"
class Helper2<U>" + uConstraint + @"
{
public static void Test2(U h2)
{
if (h2 is S s)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyDiagnostics(
// (6,19): error CS8121: An expression of type 'U' cannot be handled by a pattern of type 'S'.
// if (h2 is S s)
Diagnostic(ErrorCode.ERR_PatternWrongType, "S").WithArguments("U", "S").WithLocation(6, 19)
);
}
[Fact]
public void IsPattern_08()
{
var src = @"
class Helper2<U>
where U : allows ref struct
{
public static void Test2(U h2)
{
if (h2 is S s)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,19): error CS8121: An expression of type 'U' cannot be handled by a pattern of type 'S'.
// if (h2 is S s)
Diagnostic(ErrorCode.ERR_PatternWrongType, "S").WithArguments("U", "S").WithLocation(7, 19)
);
}
[Fact]
public void IsPattern_09()
{
var src = @"
class Helper2
{
public static void Test2(S1 h2)
{
if (h2 is S2 s2)
{
}
}
}
ref struct S1
{
}
ref struct S2
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (6,19): error CS8121: An expression of type 'S1' cannot be handled by a pattern of type 'S2'.
// if (h2 is S2 s2)
Diagnostic(ErrorCode.ERR_PatternWrongType, "S2").WithArguments("S1", "S2").WithLocation(6, 19)
);
}
[Fact]
public void IsPattern_10()
{
var src = @"
class Helper1<T, U>
where T : allows ref struct
where U : T, allows ref struct
{
public static void Test1(T h1)
{
if (h1 is U u)
{
}
}
public static void Test2(U h2)
{
if (h2 is T t)
{
}
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (8,19): error CS8121: An expression of type 'T' cannot be handled by a pattern of type 'U'.
// if (h1 is U u)
Diagnostic(ErrorCode.ERR_PatternWrongType, "U").WithArguments("T", "U").WithLocation(8, 19),
// (14,19): error CS8121: An expression of type 'U' cannot be handled by a pattern of type 'T'.
// if (h2 is T t)
Diagnostic(ErrorCode.ERR_PatternWrongType, "T").WithArguments("U", "T").WithLocation(14, 19)
);
}
[Fact]
public void IsPattern_11()
{
var src1 = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(S h1)
{
if (h1 is T t)
{
}
}
}
ref struct S
{
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseDll);
comp1.VerifyDiagnostics(
// (7,19): error CS8121: An expression of type 'S' cannot be handled by a pattern of type 'T'.
// if (h1 is T t)
Diagnostic(ErrorCode.ERR_PatternWrongType, "T").WithArguments("S", "T").WithLocation(7, 19)
);
}
[Fact]
public void IsPattern_12()
{
var src1 = @"
class Helper1<T>
where T : I1, allows ref struct
{
public static void Test1(S1 h1)
{
if (h1 is T t)
{
t.M();
}
else
{
System.Console.Write(2);
}
}
}
ref struct S : I1
{
public void M()
{
System.Console.Write(3);
}
}
struct S1 : I1
{
public void M()
{
System.Console.Write(4);
}
}
interface I1
{
void M();
}
class Program : I1
{
static void Main()
{
Helper1<S>.Test1(new S1());
Helper1<S1>.Test1(new S1());
Helper1<Program>.Test1(new S1());
}
public void M()
{
System.Console.Write(1);
}
}
";
var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp1,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "242" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
// According to
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2074169181 and
// https://github.com/dotnet/runtime/pull/101458#issuecomment-2075815858
// the following is a valid IL
verifier.VerifyIL("Helper1<T>.Test1(S1)",
@"
{
// Code size 51 (0x33)
.maxstack 1
.locals init (T V_0) //t
IL_0000: ldarg.0
IL_0001: box ""S1""
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_002c
IL_000d: ldarg.0
IL_000e: box ""S1""
IL_0013: isinst ""T""
IL_0018: unbox.any ""T""
IL_001d: stloc.0
IL_001e: ldloca.s V_0
IL_0020: constrained. ""T""
IL_0026: callvirt ""void I1.M()""
IL_002b: ret
IL_002c: ldc.i4.2
IL_002d: call ""void System.Console.Write(int)""
IL_0032: ret
}
");
var src2 = @"
class Helper2
{
static void Test2(S1 h2)
{
if (h2 is S s)
{
}
}
}
ref struct S : I1
{
}
interface I1
{
}
struct S1 : I1
{
}
";
var comp2 = CreateCompilation(src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyDiagnostics(
// (6,19): error CS8121: An expression of type 'S1' cannot be handled by a pattern of type 'S'.
// if (h2 is S s)
Diagnostic(ErrorCode.ERR_PatternWrongType, "S").WithArguments("S1", "S").WithLocation(6, 19)
);
}
[Fact]
public void AsOperator_01()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(I1 h1)
{
_ = h1 as T;
}
}
class Helper2
{
public static void Test2(I1 h2)
{
_ = h2 as S;
}
}
ref struct S : I1
{
}
interface I1
{
}
struct S1 : I1 {}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0413: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as T;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as T").WithArguments("T").WithLocation(7, 13),
// (15,13): error CS0077: The as operator must be used with a reference type or nullable type ('S' is a non-nullable value type)
// _ = h2 as S;
Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "h2 as S").WithArguments("S").WithLocation(15, 13)
);
}
[Fact]
public void AsOperator_02()
{
var src1 = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(T h1)
{
_ = h1 as I1;
}
}
class Helper2
{
public static void Test2(S h2)
{
_ = h2 as I1;
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0019: Operator 'as' cannot be applied to operands of type 'T' and 'I1'
// _ = h1 as I1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h1 as I1").WithArguments("as", "T", "I1").WithLocation(7, 13),
// (15,13): error CS0039: Cannot convert type 'S' to 'I1' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion
// _ = h2 as I1;
Diagnostic(ErrorCode.ERR_NoExplicitBuiltinConv, "h2 as I1").WithArguments("S", "I1").WithLocation(15, 13)
);
}
[Fact]
public void AsOperator_03()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(T h1)
{
_ = h1 as T;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0413: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as T;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as T").WithArguments("T").WithLocation(7, 13)
);
}
[Fact]
public void AsOperator_04()
{
var src = @"
class Helper1<T>
where T : struct, allows ref struct
{
public static void Test1(T h1)
{
_ = h1 as T;
}
}
class Helper2
{
public static void Test2(S h2)
{
_ = h2 as S;
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0413: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as T;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as T").WithArguments("T").WithLocation(7, 13),
// (15,13): error CS0077: The as operator must be used with a reference type or nullable type ('S' is a non-nullable value type)
// _ = h2 as S;
Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "h2 as S").WithArguments("S").WithLocation(15, 13)
);
}
[Theory]
[CombinatorialData]
public void AsOperator_05(bool uHasClassConstraint, bool uHasInterfaceConstraint)
{
var uConstraint = GetUConstraint(uHasClassConstraint, uHasInterfaceConstraint);
var src1 = @"
class Helper1<T, U>
where T : allows ref struct" + uConstraint + @"
{
public static void Test1(T h1)
{
_ = h1 as U;
}
}
class Helper2<U>" + uConstraint + @"
{
public static void Test2(S h2)
{
_ = h2 as U;
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
if (!uHasClassConstraint)
{
comp.VerifyDiagnostics(
// (7,13): error CS0413: The type parameter 'U' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as U;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as U").WithArguments("U").WithLocation(7, 13),
// (15,13): error CS0413: The type parameter 'U' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h2 as U;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h2 as U").WithArguments("U").WithLocation(15, 13)
);
}
else
{
comp.VerifyDiagnostics(
// (7,13): error CS0019: Operator 'as' cannot be applied to operands of type 'T' and 'U'
// _ = h1 as U;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h1 as U").WithArguments("as", "T", "U").WithLocation(7, 13),
// (15,13): error CS0019: Operator 'as' cannot be applied to operands of type 'S' and 'U'
// _ = h2 as U;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "h2 as U").WithArguments("as", "S", "U").WithLocation(15, 13)
);
}
}
[Fact]
public void AsOperator_06()
{
var src = @"
class Helper1<T, U>
where T : allows ref struct
where U : allows ref struct
{
public static void Test1(T h1)
{
_ = h1 as U;
}
}
class Helper2<U>
where U : allows ref struct
{
public static void Test2(S h2)
{
_ = h2 as U;
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (8,13): error CS0413: The type parameter 'U' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as U;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as U").WithArguments("U").WithLocation(8, 13),
// (17,13): error CS0413: The type parameter 'U' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h2 as U;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h2 as U").WithArguments("U").WithLocation(17, 13)
);
}
[Theory]
[CombinatorialData]
public void AsOperator_07(bool uHasClassConstraint, bool uHasInterfaceConstraint)
{
var uConstraint = GetUConstraint(uHasClassConstraint, uHasInterfaceConstraint);
var src = @"
class Helper1<T, U>
where T : allows ref struct" + uConstraint + @"
{
static void Test1(U h1)
{
_ = h1 as T;
}
}
class Helper2<U>" + uConstraint + @"
{
public static void Test2(U h2)
{
_ = h2 as S;
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0413: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as T;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as T").WithArguments("T").WithLocation(7, 13),
// (15,13): error CS0077: The as operator must be used with a reference type or nullable type ('S' is a non-nullable value type)
// _ = h2 as S;
Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "h2 as S").WithArguments("S").WithLocation(15, 13)
);
}
[Fact]
public void AsOperator_08()
{
var src = @"
class Helper2<U>
where U : allows ref struct
{
public static void Test2(U h2)
{
_ = h2 as S;
}
}
ref struct S : I1
{
}
interface I1
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0077: The as operator must be used with a reference type or nullable type ('S' is a non-nullable value type)
// _ = h2 as S;
Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "h2 as S").WithArguments("S").WithLocation(7, 13)
);
}
[Fact]
public void AsOperator_09()
{
var src = @"
class Helper2
{
public static void Test2(S1 h2)
{
_ = h2 as S2;
}
}
ref struct S1
{
}
ref struct S2
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (6,13): error CS0077: The as operator must be used with a reference type or nullable type ('S2' is a non-nullable value type)
// _ = h2 as S2;
Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "h2 as S2").WithArguments("S2").WithLocation(6, 13)
);
}
[Fact]
public void AsOperator_10()
{
var src = @"
class Helper1<T, U>
where T : allows ref struct
where U : T, allows ref struct
{
public static void Test1(T h1)
{
_ = h1 as U;
}
public static void Test2(U h2)
{
_ = h2 as T;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (8,13): error CS0413: The type parameter 'U' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as U;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as U").WithArguments("U").WithLocation(8, 13),
// (12,13): error CS0413: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h2 as T;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h2 as T").WithArguments("T").WithLocation(12, 13)
);
}
[Fact]
public void AsOperator_11()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(S h1)
{
_ = h1 as T;
}
}
class Helper2
{
public static void Test2(S h2)
{
_ = h2 as S;
}
}
ref struct S
{
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0413: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as T;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as T").WithArguments("T").WithLocation(7, 13),
// (15,13): error CS0077: The as operator must be used with a reference type or nullable type ('S' is a non-nullable value type)
// _ = h2 as S;
Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "h2 as S").WithArguments("S").WithLocation(15, 13)
);
}
[Fact]
public void AsOperator_12()
{
var src = @"
class Helper1<T>
where T : allows ref struct
{
public static void Test1(S1 h1)
{
_ = h1 as T;
}
}
class Helper2
{
public static void Test2(S1 h2)
{
_ = h2 as S;
}
}
ref struct S : I1
{
}
interface I1
{
}
struct S1 : I1 {}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,13): error CS0413: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
// _ = h1 as T;
Diagnostic(ErrorCode.ERR_AsWithTypeVar, "h1 as T").WithArguments("T").WithLocation(7, 13),
// (15,13): error CS0077: The as operator must be used with a reference type or nullable type ('S' is a non-nullable value type)
// _ = h2 as S;
Diagnostic(ErrorCode.ERR_AsMustHaveReferenceType, "h2 as S").WithArguments("S").WithLocation(15, 13)
);
}
[Fact]
public void IllegalCapturing_01()
{
var source = @"
ref struct R1
{
}
ref struct R2<T>(R1 r1, T t)
where T : allows ref struct
{
R1 M1() => r1;
T M2() => t;
}
";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (9,16): error CS9110: Cannot use primary constructor parameter 'r1' that has ref-like type inside an instance member
// R1 M1() => r1;
Diagnostic(ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefLike, "r1").WithArguments("r1").WithLocation(9, 16),
// (10,15): error CS9110: Cannot use primary constructor parameter 't' that has ref-like type inside an instance member
// T M2() => t;
Diagnostic(ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefLike, "t").WithArguments("t").WithLocation(10, 15)
);
}
[Fact]
public void IllegalCapturing_02()
{
var source = @"
ref struct R1
{
}
class C
{
void M<T>(R1 r1, T t)
where T : allows ref struct
{
var d1 = () => r1;
var d2 = () => t;
}
}
";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (11,24): error CS9108: Cannot use parameter 'r1' that has ref-like type inside an anonymous method, lambda expression, query expression, or local function
// var d1 = () => r1;
Diagnostic(ErrorCode.ERR_AnonDelegateCantUseRefLike, "r1").WithArguments("r1").WithLocation(11, 24),
// (12,24): error CS9108: Cannot use parameter 't' that has ref-like type inside an anonymous method, lambda expression, query expression, or local function
// var d2 = () => t;
Diagnostic(ErrorCode.ERR_AnonDelegateCantUseRefLike, "t").WithArguments("t").WithLocation(12, 24)
);
}
[Fact]
public void IllegalCapturing_03()
{
var source = @"
ref struct R1
{
}
class C
{
void M<T>(R1 r1, T t)
where T : allows ref struct
{
R1 r2 = r1;
T t2 = t;
var d1 = () => r2;
var d2 = () => t2;
}
}
";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (14,24): error CS8175: Cannot use ref local 'r2' inside an anonymous method, lambda expression, or query expression
// var d1 = () => r2;
Diagnostic(ErrorCode.ERR_AnonDelegateCantUseLocal, "r2").WithArguments("r2").WithLocation(14, 24),
// (15,24): error CS8175: Cannot use ref local 't2' inside an anonymous method, lambda expression, or query expression
// var d2 = () => t2;
Diagnostic(ErrorCode.ERR_AnonDelegateCantUseLocal, "t2").WithArguments("t2").WithLocation(15, 24)
);
}
[Fact]
public void PassingSpansToParameters_Errors()
{
var src = @"
using System;
class C
{
static void Main()
{
Span<int> s1 = stackalloc int[1];
M1(s1);
}
static void M1<T>(T s1) where T : allows ref struct
{
var obj = new C();
T s2 = M3<T>(stackalloc int[2]);
M2(ref s1, out s2); // one
M2(ref s2, out s1); // two
M2(ref s1, out s2); // three
M2(ref s2, out s1); // four
M2(y: out s2, x: ref s1); // five
M2(y: out s1, x: ref s2); // six
M2(ref s1, out s1); // okay
M2(ref s2, out s2); // okay
}
static void M2<T>(scoped ref T x, out T y)
where T : allows ref struct
{
y = default;
}
static T M3<T>(Span<int> x) where T : allows ref struct => default;
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (17,9): error CS8350: This combination of arguments to 'C.M2<T>(scoped ref T, out T)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope
// M2(ref s2, out s1); // two
Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2<T>(scoped ref T, out T)", "x").WithLocation(17, 9),
// (17,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope
// M2(ref s2, out s1); // two
Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(17, 16),
// (20,9): error CS8350: This combination of arguments to 'C.M2<T>(scoped ref T, out T)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope
// M2(ref s2, out s1); // four
Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(ref s2, out s1)").WithArguments("C.M2<T>(scoped ref T, out T)", "x").WithLocation(20, 9),
// (20,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope
// M2(ref s2, out s1); // four
Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(20, 16),
// (23,9): error CS8350: This combination of arguments to 'C.M2<T>(scoped ref T, out T)' is disallowed because it may expose variables referenced by parameter 'x' outside of their declaration scope
// M2(y: out s1, x: ref s2); // six
Diagnostic(ErrorCode.ERR_CallArgMixing, "M2(y: out s1, x: ref s2)").WithArguments("C.M2<T>(scoped ref T, out T)", "x").WithLocation(23, 9),
// (23,30): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope
// M2(y: out s1, x: ref s2); // six
Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(23, 30)
);
}
[Fact]
public void RefLikeReturnEscape1()
{
var text = @"
using System;
class Program<T> where T : allows ref struct
{
static void Main()
{
}
static ref int Test1(T arg)
{
throw null;
}
static T MayWrap(Span<int> arg)
{
return default;
}
static ref int Test3()
{
Span<int> local = stackalloc int[1];
var sp = MayWrap(local);
return ref Test1(sp);
}
}
";
CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics).VerifyDiagnostics(
// (24,30): error CS8352: Cannot use variable 'sp' in this context because it may expose referenced variables outside of their declaration scope
// return ref Test1(sp);
Diagnostic(ErrorCode.ERR_EscapeVariable, "sp").WithArguments("sp").WithLocation(24, 30),
// (24,24): error CS8347: Cannot use a result of 'Program<T>.Test1(T)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// return ref Test1(sp);
Diagnostic(ErrorCode.ERR_EscapeCall, "Test1(sp)").WithArguments("Program<T>.Test1(T)", "arg").WithLocation(24, 24)
);
}
[Fact]
public void RefLikeEscapeMixingCall()
{
var text = @"
using System;
class Program<T> where T : allows ref struct
{
static void Main()
{
}
void Test1()
{
T rOuter = default;
Span<int> inner = stackalloc int[1];
T rInner = MayWrap(ref inner);
// valid
MayAssign(ref rOuter, ref rOuter);
// error
MayAssign(ref rOuter, ref rInner);
// error
MayAssign(ref inner, ref rOuter);
}
static void MayAssign(ref Span<int> arg1, ref T arg2)
{
arg2 = MayWrap(ref arg1);
}
static void MayAssign(ref T arg1, ref T arg2)
{
arg1 = arg2;
}
static T MayWrap(ref Span<int> arg)
{
return default;
}
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (20,39): error CS8352: Cannot use variable 'rInner' in this context because it may expose referenced variables outside of their declaration scope
// MayAssign(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_EscapeVariable, "rInner").WithArguments("rInner").WithLocation(20, 39),
// (20,13): error CS8350: This combination of arguments to 'Program<T>.MayAssign(ref T, ref T)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
// MayAssign(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref rOuter, ref rInner)").WithArguments("Program<T>.MayAssign(ref T, ref T)", "arg2").WithLocation(20, 13),
// (23,27): error CS8352: Cannot use variable 'inner' in this context because it may expose referenced variables outside of their declaration scope
// MayAssign(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_EscapeVariable, "inner").WithArguments("inner").WithLocation(23, 27),
// (23,13): error CS8350: This combination of arguments to 'Program<T>.MayAssign(ref Span<int>, ref T)' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
// MayAssign(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref inner, ref rOuter)").WithArguments("Program<T>.MayAssign(ref System.Span<int>, ref T)", "arg1").WithLocation(23, 13),
// (28,32): error CS9077: Cannot return a parameter by reference 'arg1' through a ref parameter; it can only be returned in a return statement
// arg2 = MayWrap(ref arg1);
Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "arg1").WithArguments("arg1").WithLocation(28, 32),
// (28,20): error CS8347: Cannot use a result of 'Program<T>.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// arg2 = MayWrap(ref arg1);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref arg1)").WithArguments("Program<T>.MayWrap(ref System.Span<int>)", "arg").WithLocation(28, 20)
);
comp = CreateCompilation(text, targetFramework: TargetFramework.Net60, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
// (3,39): error CS8936: Feature 'allows ref struct constraint' is not available in C# 10.0. Please use language version 13.0 or greater.
// class Program<T> where T : allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(3, 39),
// (3,39): error CS9240: Target runtime doesn't support by-ref-like generics.
// class Program<T> where T : allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(3, 39),
// (20,39): error CS8352: Cannot use variable 'rInner' in this context because it may expose referenced variables outside of their declaration scope
// MayAssign(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_EscapeVariable, "rInner").WithArguments("rInner").WithLocation(20, 39),
// (20,13): error CS8350: This combination of arguments to 'Program<T>.MayAssign(ref T, ref T)' is disallowed because it may expose variables referenced by parameter 'arg2' outside of their declaration scope
// MayAssign(ref rOuter, ref rInner);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref rOuter, ref rInner)").WithArguments("Program<T>.MayAssign(ref T, ref T)", "arg2").WithLocation(20, 13),
// (23,27): error CS8352: Cannot use variable 'inner' in this context because it may expose referenced variables outside of their declaration scope
// MayAssign(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_EscapeVariable, "inner").WithArguments("inner").WithLocation(23, 27),
// (23,13): error CS8350: This combination of arguments to 'Program<T>.MayAssign(ref Span<int>, ref T)' is disallowed because it may expose variables referenced by parameter 'arg1' outside of their declaration scope
// MayAssign(ref inner, ref rOuter);
Diagnostic(ErrorCode.ERR_CallArgMixing, "MayAssign(ref inner, ref rOuter)").WithArguments("Program<T>.MayAssign(ref System.Span<int>, ref T)", "arg1").WithLocation(23, 13)
);
}
[Fact]
public void RefSafeToEscape_05()
{
var source =
@"
class Program<T> where T : allows ref struct
{
static ref T F0(T r0)
{
scoped ref T l0 = ref r0;
return ref l0; // 1
}
static ref T F1(scoped T r1)
{
scoped ref T l1 = ref r1; // 2
return ref l1; // 3
}
static ref T F2(ref T r2)
{
scoped ref T l2 = ref r2;
return ref l2; // 4
}
static ref T F3(scoped ref T r3)
{
scoped ref T l3 = ref r3;
return ref l3; // 5
}
}";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,20): error CS8157: Cannot return 'l0' by reference because it was initialized to a value that cannot be returned by reference
// return ref l0; // 1
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "l0").WithArguments("l0").WithLocation(7, 20),
// (11,31): error CS8352: Cannot use variable 'scoped T r1' in this context because it may expose referenced variables outside of their declaration scope
// scoped ref T l1 = ref r1; // 2
Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("scoped T r1").WithLocation(11, 31),
// (12,20): error CS8157: Cannot return 'l1' by reference because it was initialized to a value that cannot be returned by reference
// return ref l1; // 3
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "l1").WithArguments("l1").WithLocation(12, 20),
// (17,20): error CS8157: Cannot return 'l2' by reference because it was initialized to a value that cannot be returned by reference
// return ref l2; // 4
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "l2").WithArguments("l2").WithLocation(17, 20),
// (22,20): error CS8157: Cannot return 'l3' by reference because it was initialized to a value that cannot be returned by reference
// return ref l3; // 5
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "l3").WithArguments("l3").WithLocation(22, 20)
);
}
[Fact]
public void RefToRefStructParameter_02()
{
var source =
@"
class Program<R> where R : allows ref struct
{
static ref R F1()
{
int i = 42;
var r1 = GetR(ref i);
return ref ReturnRef(ref r1);
}
static ref R F2(ref int i)
{
var r2 = GetR(ref i);
return ref ReturnRef(ref r2);
}
static ref R ReturnRef(scoped ref R r) => throw null;
static R GetR(ref int x) => throw null;
}";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics();
}
[Fact]
public void ReturnRefToByValueParameter_01()
{
var source =
@"
using System.Diagnostics.CodeAnalysis;
class Program<S> where S : allows ref struct
{
static ref S F1([UnscopedRef] ref S x1)
{
return ref x1;
}
static ref S F2(S x2)
{
ref var y2 = ref F1(ref x2);
return ref y2; // 1
}
}";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (12,26): error CS8350: This combination of arguments to 'Program<S>.F1(ref S)' is disallowed because it may expose variables referenced by parameter 'x1' outside of their declaration scope
// ref var y2 = ref F1(ref x2);
Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x2)").WithArguments("Program<S>.F1(ref S)", "x1").WithLocation(12, 26),
// (12,33): error CS8166: Cannot return a parameter by reference 'x2' because it is not a ref parameter
// ref var y2 = ref F1(ref x2);
Diagnostic(ErrorCode.ERR_RefReturnParameter, "x2").WithArguments("x2").WithLocation(12, 33),
// (13,20): error CS8157: Cannot return 'y2' by reference because it was initialized to a value that cannot be returned by reference
// return ref y2; // 1
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "y2").WithArguments("y2").WithLocation(13, 20)
);
}
[Fact]
public void ReturnRefToByValueParameter_02()
{
var source =
@"
using System.Diagnostics.CodeAnalysis;
class Program<S> where S : allows ref struct
{
static ref S F1(ref S x1)
{
return ref x1;
}
static ref S F2(S x2)
{
ref var y2 = ref F1(ref x2);
return ref y2; // 1
}
}";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (13,20): error CS8157: Cannot return 'y2' by reference because it was initialized to a value that cannot be returned by reference
// return ref y2; // 1
Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "y2").WithArguments("y2").WithLocation(13, 20)
);
}
[Fact]
public void ReturnRefToRefStruct_RefEscape_01()
{
var source = """
public class Repro<RefStruct> where RefStruct : allows ref struct
{
private static ref RefStruct M1(ref RefStruct s1, ref RefStruct s2)
{
bool b = false;
return ref b ? ref s1 : ref s2;
}
private static ref RefStruct M2(ref RefStruct s1)
{
RefStruct s2 = default;
// RSTE of s1 is ReturnOnly
// RSTE of s2 is CurrentMethod
return ref M1(ref s1, ref s2); // 1
}
private static ref RefStruct M3(ref RefStruct s1)
{
return ref M1(ref s1, ref s1);
}
}
""";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (14,20): error CS8347: Cannot use a result of 'Repro<RefStruct>.M1(ref RefStruct, ref RefStruct)' in this context because it may expose variables referenced by parameter 's2' outside of their declaration scope
// return ref M1(ref s1, ref s2); // 1
Diagnostic(ErrorCode.ERR_EscapeCall, "M1(ref s1, ref s2)").WithArguments("Repro<RefStruct>.M1(ref RefStruct, ref RefStruct)", "s2").WithLocation(14, 20),
// (14,35): error CS8168: Cannot return local 's2' by reference because it is not a ref local
// return ref M1(ref s1, ref s2); // 1
Diagnostic(ErrorCode.ERR_RefReturnLocal, "s2").WithArguments("s2").WithLocation(14, 35)
);
}
[Fact]
public void RefStructProperty_01()
{
var source =
@"
class C<Rint, Robject>
where Rint : allows ref struct
where Robject : allows ref struct
{
Robject this[Rint r] => default;
static Robject F1(C<Rint, Robject> c)
{
int i = 1;
var r1 = GetRint(ref i);
return c[r1]; // 1
}
static Robject F2(C<Rint, Robject> c)
{
var r2 = GetRint();
return c[r2];
}
static Rint GetRint(ref int x) => throw null;
static Rint GetRint() => throw null;
}";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (11,16): error CS8347: Cannot use a result of 'C<Rint, Robject>.this[Rint]' in this context because it may expose variables referenced by parameter 'r' outside of their declaration scope
// return c[r1]; // 1
Diagnostic(ErrorCode.ERR_EscapeCall, "c[r1]").WithArguments("C<Rint, Robject>.this[Rint]", "r").WithLocation(11, 16),
// (11,18): error CS8352: Cannot use variable 'r1' in this context because it may expose referenced variables outside of their declaration scope
// return c[r1]; // 1
Diagnostic(ErrorCode.ERR_EscapeVariable, "r1").WithArguments("r1").WithLocation(11, 18)
);
}
[Fact]
public void MethodArgumentsMustMatch_08()
{
var source =
@"
using static Helper;
class Helper
{
public static void F0(__arglist) { }
}
class Program<R>
where R : allows ref struct
{
static void F1()
{
var x = GetR();
int i = 1;
var y = GetR(ref i);
F0(__arglist(ref x)); // 1
F0(__arglist(ref y));
F0(__arglist(ref x, ref x)); // 2
F0(__arglist(ref x, ref y)); // 3
F0(__arglist(ref y, ref x)); // 4
F0(__arglist(ref y, ref y));
}
static R GetR(ref int x) => throw null;
static R GetR() => throw null;
}";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (19,9): error CS8350: This combination of arguments to 'Helper.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope
// F0(__arglist(ref x, ref y)); // 3
Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref x, ref y))").WithArguments("Helper.F0(__arglist)", "__arglist").WithLocation(19, 9),
// (19,33): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope
// F0(__arglist(ref x, ref y)); // 3
Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(19, 33),
// (20,9): error CS8350: This combination of arguments to 'Helper.F0(__arglist)' is disallowed because it may expose variables referenced by parameter '__arglist' outside of their declaration scope
// F0(__arglist(ref y, ref x)); // 4
Diagnostic(ErrorCode.ERR_CallArgMixing, "F0(__arglist(ref y, ref x))").WithArguments("Helper.F0(__arglist)", "__arglist").WithLocation(20, 9),
// (20,26): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope
// F0(__arglist(ref y, ref x)); // 4
Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(20, 26)
);
}
[Fact]
public void IsPatternMatchingDoesNotCopyEscapeScopes_05()
{
CreateCompilation(@"
using System;
public interface IR<R> where R : IR<R>, allows ref struct
{
public R RProp {get;}
public S<R> SProp {get;}
public abstract static implicit operator R(Span<int> span);
}
public struct S<R> where R : IR<R>, allows ref struct
{
public R RProp => throw null;
public S<R> SProp => throw null;
}
public class C<R> where R : IR<R>, allows ref struct
{
public void M1(ref R r, ref S<R> s)
{
R outer = stackalloc int[100];
if (outer is { RProp.RProp: var rr0 }) r = rr0; // error
if (outer is { SProp.RProp: var sr0 }) r = sr0; // OK
if (outer is { SProp.SProp: var ss0 }) s = ss0; // OK
if (outer is { RProp.SProp: var rs0 }) s = rs0; // OK
if (outer is { RProp: { RProp: var rr1 }}) r = rr1; // error
if (outer is { SProp: { RProp: var sr1 }}) r = sr1; // OK
if (outer is { SProp: { SProp: var ss1 }}) s = ss1; // OK
if (outer is { RProp: { SProp: var rs1 }}) s = rs1; // OK
}
}", targetFramework: s_targetFrameworkSupportingByRefLikeGenerics).VerifyDiagnostics(
// (19,52): error CS8352: Cannot use variable 'rr0' in this context because it may expose referenced variables outside of their declaration scope
// if (outer is { RProp.RProp: var rr0 }) r = rr0; // error
Diagnostic(ErrorCode.ERR_EscapeVariable, "rr0").WithArguments("rr0").WithLocation(19, 52),
// (23,56): error CS8352: Cannot use variable 'rr1' in this context because it may expose referenced variables outside of their declaration scope
// if (outer is { RProp: { RProp: var rr1 }}) r = rr1; // error
Diagnostic(ErrorCode.ERR_EscapeVariable, "rr1").WithArguments("rr1").WithLocation(23, 56)
);
}
[Fact]
public void CasePatternMatchingDoesNotCopyEscapeScopes_02()
{
CreateCompilation(@"
using System;
public interface IR<R> where R : IR<R>, allows ref struct
{
public R Prop {get;}
public void Deconstruct(out R X, out R Y);
public abstract static implicit operator R(Span<int> span);
}
public class C<R> where R : struct, IR<R>, allows ref struct
{
public R M1()
{
R outer = stackalloc int[100];
switch (outer)
{
case { Prop: var x }: return x; // error 1
}
}
public R M2()
{
R outer = stackalloc int[100];
switch (outer)
{
case { Prop: R x }: return x; // error 2
}
}
public R M3()
{
R outer = stackalloc int[100];
switch (outer)
{
case (var x, var y): return x; // error 3
}
}
public R M4()
{
R outer = stackalloc int[100];
switch (outer)
{
case (R x, R y): return x; // error 4
}
}
public R M5()
{
R outer = stackalloc int[100];
switch (outer)
{
case var (x, y): return x; // error 5
}
}
public R M6()
{
R outer = stackalloc int[100];
switch (outer)
{
case { } x: return x; // error 6
}
}
public R M7()
{
R outer = stackalloc int[100];
switch (outer)
{
case (_, _) x: return x; // error 7
}
}
}
", targetFramework: s_targetFrameworkSupportingByRefLikeGenerics).VerifyDiagnostics(
// (16,42): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope
// case { Prop: var x }: return x; // error 1
Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(16, 42),
// (24,40): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope
// case { Prop: R x }: return x; // error 2
Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(24, 40),
// (32,41): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope
// case (var x, var y): return x; // error 3
Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(32, 41),
// (40,37): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope
// case (R x, R y): return x; // error 4
Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(40, 37),
// (48,37): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope
// case var (x, y): return x; // error 5
Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(48, 37),
// (56,32): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope
// case { } x: return x; // error 6
Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(56, 32),
// (64,35): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope
// case (_, _) x: return x; // error 7
Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(64, 35)
);
}
[Fact]
public void RefLikeScopeEscapeThis()
{
var text = @"
using System;
class Program<S1> where S1 : IS1<S1>, allows ref struct
{
static void Main()
{
Span<int> outer = default;
S1 x = MayWrap(ref outer);
{
Span<int> inner = stackalloc int[1];
// valid
x = S1.NotSlice(1);
// valid
x = MayWrap(ref outer).Slice(1);
// error
x = MayWrap(ref inner).Slice(1);
}
}
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
}
interface IS1<S1> where S1 : IS1<S1>, allows ref struct
{
public abstract static S1 NotSlice(int x);
public S1 Slice(int x);
}
";
var comp = CreateCompilation(text, targetFramework: TargetFramework.Net60, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
// (31,50): error CS8936: Feature 'allows ref struct constraint' is not available in C# 10.0. Please use language version 13.0 or greater.
// interface IS1<S1> where S1 : IS1<S1>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(31, 50),
// (31,50): error CS9240: Target runtime doesn't support by-ref-like generics.
// interface IS1<S1> where S1 : IS1<S1>, allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(31, 50),
// (3,50): error CS8936: Feature 'allows ref struct constraint' is not available in C# 10.0. Please use language version 13.0 or greater.
// class Program<S1> where S1 : IS1<S1>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(3, 50),
// (3,50): error CS9240: Target runtime doesn't support by-ref-like generics.
// class Program<S1> where S1 : IS1<S1>, allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(3, 50),
// (33,35): error CS8703: The modifier 'abstract' is not valid for this item in C# 10.0. Please use language version '11.0' or greater.
// public abstract static S1 NotSlice(int x);
Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "NotSlice").WithArguments("abstract", "10.0", "11.0").WithLocation(33, 35),
// (15,21): error CS8936: Feature 'static abstract members in interfaces' is not available in C# 10.0. Please use language version 11.0 or greater.
// x = S1.NotSlice(1);
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "S1").WithArguments("static abstract members in interfaces", "11.0").WithLocation(15, 21),
// (21,33): error CS8352: Cannot use variable 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x = MayWrap(ref inner).Slice(1);
Diagnostic(ErrorCode.ERR_EscapeVariable, "inner").WithArguments("inner").WithLocation(21, 33),
// (21,21): error CS8347: Cannot use a result of 'Program<S1>.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x = MayWrap(ref inner).Slice(1);
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program<S1>.MayWrap(ref System.Span<int>)", "arg").WithLocation(21, 21)
);
}
[Fact]
public void RefLikeScopeEscapeThisRef()
{
var text = @"
using System;
class Program<S1> where S1 : IS1<S1>, allows ref struct
{
static void Main()
{
Span<int> outer = default;
ref S1 x = ref MayWrap(ref outer)[0];
{
Span<int> inner = stackalloc int[1];
// valid
x[0] = MayWrap(ref outer).Slice(1)[0];
// error, technically rules for this case can be relaxed,
// but ref-like typed ref-returning properties are nearly impossible to implement in a useful way
//
x[0] = MayWrap(ref inner).Slice(1)[0];
// error, technically rules for this case can be relaxed,
// but ref-like typed ref-returning properties are nearly impossible to implement in a useful way
//
x[x] = MayWrap(ref inner).Slice(1)[0];
// error
x.ReturnsRefArg(ref x) = MayWrap(ref inner).Slice(1)[0];
}
}
static S1 MayWrap(ref Span<int> arg)
{
return default;
}
}
interface IS1<S1> where S1 : IS1<S1>, allows ref struct
{
public ref S1 this[int i] {get;}
public ref S1 this[S1 i] {get;}
public ref S1 ReturnsRefArg(ref S1 arg);
public S1 Slice(int x);
}
";
var comp = CreateCompilation(new[] { text, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net60, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
// 0.cs(3,46): error CS8936: Feature 'allows ref struct constraint' is not available in C# 10.0. Please use language version 13.0 or greater.
// class Program<S1> where S1 : IS1<S1>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(3, 46),
// 0.cs(3,46): error CS9240: Target runtime doesn't support by-ref-like generics.
// class Program<S1> where S1 : IS1<S1>, allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(3, 46),
// 0.cs(38,46): error CS8936: Feature 'allows ref struct constraint' is not available in C# 10.0. Please use language version 13.0 or greater.
// interface IS1<S1> where S1 : IS1<S1>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(38, 46),
// 0.cs(38,46): error CS9240: Target runtime doesn't support by-ref-like generics.
// interface IS1<S1> where S1 : IS1<S1>, allows ref struct
Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, "ref struct").WithLocation(38, 46),
// 0.cs(20,32): error CS8352: Cannot use variable 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x[0] = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeVariable, "inner").WithArguments("inner").WithLocation(20, 32),
// 0.cs(20,20): error CS8347: Cannot use a result of 'Program<S1>.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x[0] = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program<S1>.MayWrap(ref System.Span<int>)", "arg").WithLocation(20, 20),
// 0.cs(25,32): error CS8352: Cannot use variable 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x[x] = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeVariable, "inner").WithArguments("inner").WithLocation(25, 32),
// 0.cs(25,20): error CS8347: Cannot use a result of 'Program<S1>.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x[x] = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program<S1>.MayWrap(ref System.Span<int>)", "arg").WithLocation(25, 20),
// 0.cs(28,50): error CS8352: Cannot use variable 'inner' in this context because it may expose referenced variables outside of their declaration scope
// x.ReturnsRefArg(ref x) = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeVariable, "inner").WithArguments("inner").WithLocation(28, 50),
// 0.cs(28,38): error CS8347: Cannot use a result of 'Program<S1>.MayWrap(ref Span<int>)' in this context because it may expose variables referenced by parameter 'arg' outside of their declaration scope
// x.ReturnsRefArg(ref x) = MayWrap(ref inner).Slice(1)[0];
Diagnostic(ErrorCode.ERR_EscapeCall, "MayWrap(ref inner)").WithArguments("Program<S1>.MayWrap(ref System.Span<int>)", "arg").WithLocation(28, 38)
);
}
[Fact]
public void RefAssignValueScopeMismatch_05()
{
var source =
@"
class Program<S> where S : allows ref struct
{
static S F()
{
S s1 = default;
scoped ref S r1 = ref s1;
int i = 0;
S s2 = GetS(ref i);
ref S r2 = ref s2;
r2 = ref r1; // 1
r2 = s2;
return s1;
}
static S GetS(ref int i) => throw null;
}
";
var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics(
// (11,9): error CS9096: Cannot ref-assign 'r1' to 'r2' because 'r1' has a wider value escape scope than 'r2' allowing assignment through 'r2' of values with narrower escape scopes than 'r1'.
// r2 = ref r1; // 1
Diagnostic(ErrorCode.ERR_RefAssignValEscapeWider, "r2 = ref r1").WithArguments("r2", "r1").WithLocation(11, 9)
);
}
[Fact]
public void RefLikeObjInitializers1()
{
var text = @"
using System;
class Program<S2> where S2 : IS2, new(), allows ref struct
{
static void Main()
{
}
static S2 Test1()
{
S1 outer = default;
S1 inner = stackalloc int[1];
var x1 = new S2() { Field1 = outer, Field2 = inner };
// error
return x1;
}
static S2 Test2()
{
S1 outer = default;
S1 inner = stackalloc int[1];
var x2 = new S2() { Field1 = inner, Field2 = outer };
// error
return x2;
}
static S2 Test3()
{
S1 outer = default;
S1 inner = stackalloc int[1];
var x3 = new S2() { Field1 = outer, Field2 = outer };
// ok
return x3;
}
}
public ref struct S1
{
public static implicit operator S1(Span<int> o) => default;
}
public interface IS2
{
public S1 Field1 {get;set;}
public S1 Field2 {get;set;}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics).VerifyEmitDiagnostics(
// (18,20): error CS8352: Cannot use variable 'x1' in this context because it may expose referenced variables outside of their declaration scope
// return x1;
Diagnostic(ErrorCode.ERR_EscapeVariable, "x1").WithArguments("x1").WithLocation(18, 20),
// (29,20): error CS8352: Cannot use variable 'x2' in this context because it may expose referenced variables outside of their declaration scope
// return x2;
Diagnostic(ErrorCode.ERR_EscapeVariable, "x2").WithArguments("x2").WithLocation(29, 20)
);
}
[Fact]
public void RefLikeObjInitializersIndexer1()
{
var text = @"
using System;
class Program<S2> where S2 : IS2, new(), allows ref struct
{
static void Main()
{
}
static S2 Test1()
{
S1 outer = default;
S1 inner = stackalloc int[1];
var x1 = new S2() { [inner] = outer, Field2 = outer };
// error
return x1;
}
static S2 Test2()
{
S1 outer = default;
S1 inner = stackalloc int[1];
S2 result;
// error
result = new S2() { [outer] = inner, Field2 = outer };
return result;
}
static S2 Test3()
{
S1 outer = default;
S1 inner = stackalloc int[1];
var x3 = new S2() { [outer] = outer, Field2 = outer };
// ok
return x3;
}
}
public ref struct S1
{
public static implicit operator S1(Span<int> o) => default;
}
public interface IS2
{
public S1 this[S1 i] {get;set;}
public S1 Field2 {get;set;}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics).VerifyEmitDiagnostics(
// (18,16): error CS8352: Cannot use variable 'x1' in this context because it may expose referenced variables outside of their declaration scope
// return x1;
Diagnostic(ErrorCode.ERR_EscapeVariable, "x1").WithArguments("x1").WithLocation(18, 16),
// (29,27): error CS8352: Cannot use variable '[outer] = inner' in this context because it may expose referenced variables outside of their declaration scope
// result = new S2() { [outer] = inner, Field2 = outer };
Diagnostic(ErrorCode.ERR_EscapeVariable, "{ [outer] = inner, Field2 = outer }").WithArguments("[outer] = inner").WithLocation(29, 27)
);
}
[Fact]
public void NullCheck_01()
{
var src = @"
public class Helper
{
public static bool Test1<T>(T value)
where T : allows ref struct
{
return value == null;
}
public static bool Test2<T>(T value)
where T : allows ref struct
{
return null == value;
}
public static bool Test3<T>(T value)
where T : allows ref struct
{
return value != null;
}
public static bool Test4<T>(T value)
where T : allows ref struct
{
return null != value;
}
}
ref struct S
{
}
class Program
{
static void Main()
{
System.Console.Write(Helper.Test1<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<Program>(new Program()));
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "False False True True True True False False False False True True" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper.Test1<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test2<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test3<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test4<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
}
[Fact]
public void NullCheck_02()
{
var src = @"
public class Helper
{
public static bool Test1<T>(T value)
where T : allows ref struct
{
return value is null;
}
public static bool Test3<T>(T value)
where T : allows ref struct
{
return value is not null;
}
}
ref struct S
{
}
class Program
{
static void Main()
{
System.Console.Write(Helper.Test1<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(new Program()));
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "False True True False False True" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper.Test1<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test3<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
}
[Fact]
public void NullCheck_03()
{
var src = @"
public class Helper
{
public static bool Test1<T>(T value, object o)
where T : allows ref struct
{
return value == o;
}
public static bool Test2<T>(T value, object o)
where T : allows ref struct
{
return o == value;
}
public static bool Test3<T>(T value, object o)
where T : allows ref struct
{
return value != o;
}
public static bool Test4<T>(T value, object o)
where T : allows ref struct
{
return o != value;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,16): error CS0019: Operator '==' cannot be applied to operands of type 'T' and 'object'
// return value == o;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "value == o").WithArguments("==", "T", "object").WithLocation(7, 16),
// (13,16): error CS0019: Operator '==' cannot be applied to operands of type 'object' and 'T'
// return o == value;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "o == value").WithArguments("==", "object", "T").WithLocation(13, 16),
// (19,16): error CS0019: Operator '!=' cannot be applied to operands of type 'T' and 'object'
// return value != o;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "value != o").WithArguments("!=", "T", "object").WithLocation(19, 16),
// (25,16): error CS0019: Operator '!=' cannot be applied to operands of type 'object' and 'T'
// return o != value;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "o != value").WithArguments("!=", "object", "T").WithLocation(25, 16)
);
}
[Fact]
public void NullCheck_04()
{
var src = @"
public class Helper
{
const object o = null;
public static bool Test1<T>(T value)
where T : allows ref struct
{
return value == o;
}
public static bool Test2<T>(T value)
where T : allows ref struct
{
return o == value;
}
public static bool Test3<T>(T value)
where T : allows ref struct
{
return value != o;
}
public static bool Test4<T>(T value)
where T : allows ref struct
{
return o != value;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (9,16): error CS0019: Operator '==' cannot be applied to operands of type 'T' and 'object'
// return value == o;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "value == o").WithArguments("==", "T", "object").WithLocation(9, 16),
// (15,16): error CS0019: Operator '==' cannot be applied to operands of type 'object' and 'T'
// return o == value;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "o == value").WithArguments("==", "object", "T").WithLocation(15, 16),
// (21,16): error CS0019: Operator '!=' cannot be applied to operands of type 'T' and 'object'
// return value != o;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "value != o").WithArguments("!=", "T", "object").WithLocation(21, 16),
// (27,16): error CS0019: Operator '!=' cannot be applied to operands of type 'object' and 'T'
// return o != value;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "o != value").WithArguments("!=", "object", "T").WithLocation(27, 16)
);
}
[Fact]
public void NullCheck_05()
{
var src = @"
public class Helper
{
public static bool Test1<T>(T value)
where T : allows ref struct
{
if (value == null)
{
return true;
}
else
{
return false;
}
}
public static bool Test2<T>(T value)
where T : allows ref struct
{
if (null == value)
{
return true;
}
else
{
return false;
}
}
public static bool Test3<T>(T value)
where T : allows ref struct
{
if (value != null)
{
return true;
}
else
{
return false;
}
}
public static bool Test4<T>(T value)
where T : allows ref struct
{
if (null != value)
{
return true;
}
else
{
return false;
}
}
}
ref struct S
{
}
class Program
{
static void Main()
{
System.Console.Write(Helper.Test1<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<Program>(new Program()));
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "False False True True True True False False False False True True" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper.Test1<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test2<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test3<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test4<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
}
[Fact]
public void NullCheck_06()
{
var src = @"
public class Helper
{
public static bool Test1<T>(T value)
where T : struct, allows ref struct
{
return value == null;
}
public static bool Test2<T>(T value)
where T : struct, allows ref struct
{
return null == value;
}
public static bool Test3<T>(T value)
where T : struct, allows ref struct
{
return value != null;
}
public static bool Test4<T>(T value)
where T : struct, allows ref struct
{
return null != value;
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,16): error CS0019: Operator '==' cannot be applied to operands of type 'T' and '<null>'
// return value == null;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "value == null").WithArguments("==", "T", "<null>").WithLocation(7, 16),
// (13,16): error CS0019: Operator '==' cannot be applied to operands of type '<null>' and 'T'
// return null == value;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "null == value").WithArguments("==", "<null>", "T").WithLocation(13, 16),
// (19,16): error CS0019: Operator '!=' cannot be applied to operands of type 'T' and '<null>'
// return value != null;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "value != null").WithArguments("!=", "T", "<null>").WithLocation(19, 16),
// (25,16): error CS0019: Operator '!=' cannot be applied to operands of type '<null>' and 'T'
// return null != value;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "null != value").WithArguments("!=", "<null>", "T").WithLocation(25, 16)
);
}
[Fact]
public void NullCheck_07()
{
var src = @"
public class Helper
{
public static bool Test1<T>(T value)
where T : allows ref struct
{
return !(value != null);
}
public static bool Test2<T>(T value)
where T : allows ref struct
{
return !(null != value);
}
public static bool Test3<T>(T value)
where T : allows ref struct
{
return !(value == null);
}
public static bool Test4<T>(T value)
where T : allows ref struct
{
return !(null == value);
}
}
ref struct S
{
}
class Program
{
static void Main()
{
System.Console.Write(Helper.Test1<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<Program>(new Program()));
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "False False True True True True False False False False True True" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper.Test1<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test2<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test3<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test4<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
}
[Fact]
public void NullCheck_08()
{
var src = @"
public class Helper
{
public static bool Test1<T>(T value)
where T : allows ref struct
{
return !(value is not null);
}
public static bool Test3<T>(T value)
where T : allows ref struct
{
return !(value is null);
}
}
ref struct S
{
}
class Program
{
static void Main()
{
System.Console.Write(Helper.Test1<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(new Program()));
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "False True True False False True" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper.Test1<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test3<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
}
[Fact]
public void NullCheck_09()
{
var src = @"
public class Helper
{
public static bool Test1<T>(T value)
where T : allows ref struct
{
if (!(value != null))
{
return true;
}
else
{
return false;
}
}
public static bool Test2<T>(T value)
where T : allows ref struct
{
if (!(null != value))
{
return true;
}
else
{
return false;
}
}
public static bool Test3<T>(T value)
where T : allows ref struct
{
if (!(value == null))
{
return true;
}
else
{
return false;
}
}
public static bool Test4<T>(T value)
where T : allows ref struct
{
if (!(null == value))
{
return true;
}
else
{
return false;
}
}
}
ref struct S
{
}
class Program
{
static void Main()
{
System.Console.Write(Helper.Test1<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<S>(new S()));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<Program>(null));
System.Console.Write(' ');
System.Console.Write(Helper.Test1<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test2<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test3<Program>(new Program()));
System.Console.Write(' ');
System.Console.Write(Helper.Test4<Program>(new Program()));
}
}
";
var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "False False True True True True False False False False True True" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("Helper.Test1<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test2<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test3<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
verifier.VerifyIL("Helper.Test4<T>(T)",
@"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldarg.0
IL_0001: box ""T""
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.1
IL_0009: ret
IL_000a: ldc.i4.0
IL_000b: ret
}
");
}
[Fact]
public void NullCoalescingAssignment_01()
{
var text = @"
class Program
{
static void Test<T>()
where T : I1<T>, allows ref struct
{
var x = new C<T>();
System.Console.Write(x.PT.P);
x._pT.P++;
System.Console.Write("" "");
System.Console.Write(x.PT.P);
}
static void Main()
{
Test<S1>();
System.Console.Write("" "");
Test<S2>();
System.Console.Write("" "");
Test<C3>();
}
}
ref struct C<T>
where T : I1<T>, allows ref struct
{
public T _pT;
public T PT
{
get
{
return _pT ??= T.Create();
}
}
}
public ref struct S1 : I1<S1>
{
public int P {get;set;}
public static S1 Create() => throw null;
}
public struct S2 : I1<S2>
{
public int P {get;set;}
public static S2 Create() => throw null;
}
public class C3 : I1<C3>
{
public int P {get;set;}
public static C3 Create() => new C3();
}
public interface I1<T>
where T : I1<T>, allows ref struct
{
public int P {get;set;}
abstract static T Create();
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "0 1 0 1 0 1" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify.WithILVerifyMessage(
@"[get_PT]: Call not allowed on abstract methods. { Offset = 0x16 }
[get_PT]: Missing callvirt following constrained prefix. { Offset = 0x16 }") :
Verification.Skipped).VerifyDiagnostics();
verifier.VerifyIL("C<T>.PT.get", @"
{
// Code size 38 (0x26)
.maxstack 3
.locals init (T V_0,
T V_1)
IL_0000: ldarg.0
IL_0001: ldfld ""T C<T>._pT""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: box ""T""
IL_000d: brtrue.s IL_0024
IL_000f: ldarg.0
IL_0010: constrained. ""T""
IL_0016: call ""T I1<T>.Create()""
IL_001b: dup
IL_001c: stloc.1
IL_001d: stfld ""T C<T>._pT""
IL_0022: ldloc.1
IL_0023: ret
IL_0024: ldloc.0
IL_0025: ret
}
");
}
[Fact]
public void NullCoalescingAssignment_02()
{
var text = @"
ref struct C<T>
where T : struct, I1<T>, allows ref struct
{
public T _pT;
public T PT
{
get
{
return _pT ??= T.Create();
}
}
}
public interface I1<T>
where T : I1<T>, allows ref struct
{
public int P {get;set;}
abstract static T Create();
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (10,20): error CS0019: Operator '??=' cannot be applied to operands of type 'T' and 'T'
// return _pT ??= T.Create();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "_pT ??= T.Create()").WithArguments("??=", "T", "T").WithLocation(10, 20)
);
}
[Fact]
public void NullCoalescingAssignment_03()
{
var comp = CreateCompilation(@"
using System;
class C
{
void M<T>() where T : allows ref struct
{
T s1 = GetT<T>(stackalloc int[1]);
T s2 = GetT<T>(new Span<int>());
s2 = (s2 ??= GetT<T>(new Span<int>()));
s2 = (s1 ??= s2);
}
T GetT<T>(Span<int> s) where T : allows ref struct => default;
}
", targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (11,15): error CS8352: Cannot use variable 's1' in this context because it may expose referenced variables outside of their declaration scope
// s2 = (s1 ??= s2);
Diagnostic(ErrorCode.ERR_EscapeVariable, "s1 ??= s2").WithArguments("s1").WithLocation(11, 15)
);
}
[Fact]
public void NullCoalescingAssignment_04()
{
var comp = CreateCompilation(@"
class C
{
static T F<T>() where T : allows ref struct
{
scoped T s1 = default;
T s2 = default;
s2 ??= s1;
return s2;
}
}
", targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void NullCoalescingAssignment_05()
{
var comp = CreateCompilation(@"
class C
{
static T F<T>() where T : allows ref struct
{
T s1 = default;
T s2 = default;
s2 ??= s1;
return s2;
}
}
", targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyEmitDiagnostics();
}
[Theory]
[CombinatorialData]
public void ObjectCreation_01(bool addStructConstraint)
{
var text = @"
class Program
{
static T Test<T>()
where T : " + (addStructConstraint ? "struct, I1" : "I1, new()") + @", allows ref struct
{
return new T() { P = 123 };
}
static void Main()
{
System.Console.Write(Test<S>().P);
}
}
public ref struct S : I1
{
public int P {get;set;}
}
public interface I1
{
public int P {get;set;}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void ObjectCreation_02(bool addStructConstraint)
{
var text = @"
class Program
{
static T Test<T>()
where T : " + (addStructConstraint ? "struct, I1" : "I1, new()") + @", allows ref struct
{
return new T() { C = { P = 123 } };
}
static void Main()
{
System.Console.Write(Test<S>().C.P);
}
}
public class C
{
public int P {get;set;}
}
public ref struct S : I1
{
private C _c;
public C C
{
get
{
return _c ??= new C();
}
}
}
public interface I1
{
public C C {get;}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void ObjectCreation_03()
{
var text = @"
class Program
{
static C<T> Test<T>()
where T : I1<T>, allows ref struct
{
return new C<T>() { PT = { P = 123 } };
}
static void Main()
{
System.Console.Write(Test<S1>().PT.P);
System.Console.Write("" "");
System.Console.Write(Test<S2>().PT.P);
System.Console.Write("" "");
System.Console.Write(Test<C3>().PT.P);
}
}
ref struct C<T>
where T : I1<T>, allows ref struct
{
private T _pT;
public T PT
{
get
{
return _pT ??= T.Create();
}
}
}
public ref struct S1 : I1<S1>
{
public int P {get;set;}
public static S1 Create() => throw null;
}
public struct S2 : I1<S2>
{
public int P {get;set;}
public static S2 Create() => throw null;
}
public class C3 : I1<C3>
{
public int P {get;set;}
public static C3 Create() => new C3();
}
public interface I1<T>
where T : I1<T>, allows ref struct
{
public int P {get;set;}
abstract static T Create();
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "0 0 123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify.WithILVerifyMessage(
@"[Test]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x20 }
[get_PT]: Call not allowed on abstract methods. { Offset = 0x16 }
[get_PT]: Missing callvirt following constrained prefix. { Offset = 0x16 }") :
Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void ObjectCreation_04()
{
var text = @"
class Program
{
static C<T> Test<T>()
where T : struct, I1, allows ref struct
{
return new C<T>() { PT = { P = 123 } };
}
static void Main()
{
System.Console.Write(Test<S>().PT.P);
}
}
ref struct C<T>
where T : struct, I1, allows ref struct
{
public T PT {get;set;}
}
public ref struct S : I1
{
public int P {get;set;}
}
public interface I1
{
public int P {get;set;}
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (7,29): error CS1918: Members of property 'C<T>.PT' of type 'T' cannot be assigned with an object initializer because it is of a value type
// return new C<T>() { PT = { P = 123 } };
Diagnostic(ErrorCode.ERR_ValueTypePropertyInObjectInitializer, "PT").WithArguments("C<T>.PT", "T").WithLocation(7, 29)
);
}
[Theory]
[CombinatorialData]
public void ObjectCreation_05(bool addStructConstraint)
{
var text = @"
class Program
{
static T Test<T>()
where T : " + (addStructConstraint ? "struct, I1" : "I1, new()") + @", allows ref struct
{
return new T() { 100, 20, 3 };
}
static void Main()
{
System.Console.Write(Test<S>().P);
System.Console.Write("" "");
System.Console.Write((new S() { 200, 40, 6 }).P);
System.Console.Write("" "");
System.Console.Write((new S2() { 300, 60, 9 }).P);
}
}
public ref struct S : I1
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
}
public struct S2 : I1
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
}
public interface I1 : System.Collections.IEnumerable
{
public void Add(int x);
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "123 246 369" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void ObjectCreation_06(bool addStructConstraint)
{
var text = @"
class Program
{
static T Test<T>()
where T : " + (addStructConstraint ? "struct, I1" : "I1, new()") + @", allows ref struct
{
return new T() { C = { 100, 20, 3 } };
}
static void Main()
{
System.Console.Write(Test<S>().C.P);
}
}
public class C : System.Collections.IEnumerable
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
}
public ref struct S : I1
{
private C _c;
public C C
{
get
{
return _c ??= new C();
}
}
}
public interface I1
{
public C C {get;}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void ObjectCreation_07()
{
var text = @"
class Program
{
static C<T> Test<T>()
where T : I1<T>, allows ref struct
{
return new C<T>() { PT = { 100, 20, 3 } };
}
static void Main()
{
System.Console.Write(Test<S1>().PT.P);
System.Console.Write("" "");
System.Console.Write(Test<S2>().PT.P);
System.Console.Write("" "");
System.Console.Write(Test<C3>().PT.P);
}
}
ref struct C<T>
where T : I1<T>, allows ref struct
{
private T _pT;
public T PT
{
get
{
return _pT ??= T.Create();
}
}
}
public ref struct S1 : I1<S1>
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
public static S1 Create() => default;
}
public struct S2 : I1<S2>
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
public static S2 Create() => default;
}
public class C3 : I1<C3>
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
public static C3 Create() => new C3();
}
public interface I1<T> : System.Collections.IEnumerable
where T : I1<T>, allows ref struct
{
public void Add(int x);
abstract static T Create();
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "0 0 123" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ?
Verification.FailsILVerify.WithILVerifyMessage(
@"[Test]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x4d }
[get_PT]: Call not allowed on abstract methods. { Offset = 0x16 }
[get_PT]: Missing callvirt following constrained prefix. { Offset = 0x16 }
[Create]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }") :
Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void ObjectCreation_08()
{
var text = @"
class Program
{
static C<T> Test<T>()
where T : struct, I1, allows ref struct
{
return new C<T>() { PT = { 100, 20, 3 } };
}
}
ref struct C<T>
where T : struct, I1, allows ref struct
{
public T PT {get;set;}
}
public interface I1 : System.Collections.IEnumerable
{
public void Add(int x);
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp.VerifyDiagnostics(
// (7,29): error CS1918: Members of property 'C<T>.PT' of type 'T' cannot be assigned with an object initializer because it is of a value type
// return new C<T>() { PT = { P = 123 } };
Diagnostic(ErrorCode.ERR_ValueTypePropertyInObjectInitializer, "PT").WithArguments("C<T>.PT", "T").WithLocation(7, 29)
);
}
[Fact]
public void ObjectCreation_LanguageVersion_05()
{
var text1 = @"
public ref struct S : I1
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
}
public struct S2 : I1
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
}
public interface I1 : System.Collections.IEnumerable
{
public void Add(int x);
}
";
var comp1 = CreateCompilation(text1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var text2 = @"
class Program
{
static T Test<T>()
where T : I1, new(), allows ref struct
{
return new T() { 100, 20, 3 };
}
static void Main()
{
System.Console.Write((new S() { 200, 40, 6 }).P);
}
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp2 = CreateCompilation(text2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (5,37): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where T : I1, new(), allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(5, 37),
// (12,39): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// System.Console.Write((new S() { 200, 40, 6 }).P);
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "{ 200, 40, 6 }").WithArguments("ref struct interfaces", "13.0").WithLocation(12, 39),
// (20,62): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public static T CreateInstance<T>() where T : allows ref struct => default;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(20, 62)
);
comp2 = CreateCompilation(text2 + text1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (5,37): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where T : I1, new(), allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(5, 37),
// (20,62): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// public static T CreateInstance<T>() where T : allows ref struct => default;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(20, 62),
// (24,23): error CS9202: Feature 'ref struct interfaces' is not available in C# 12.0. Please use language version 13.0 or greater.
// public ref struct S : I1
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "I1").WithArguments("ref struct interfaces", "13.0").WithLocation(24, 23)
);
comp2 = CreateCompilation(text2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(text2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
var text3 = @"
class Program
{
static void Main()
{
System.Console.Write((new S2() { 200, 40, 6 }).P);
}
}
";
var comp3 = CreateCompilation(text3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics();
comp3 = CreateCompilation(text3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp3.VerifyEmitDiagnostics();
comp3 = CreateCompilation(text3, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp3.VerifyEmitDiagnostics();
}
[Fact]
public void ObjectCreation_LanguageVersion_07()
{
var text1 = @"
public ref struct C<T>
where T : I1<T>, allows ref struct
{
public T PT => throw null;
}
public interface I1<T> : System.Collections.IEnumerable
where T : I1<T>, allows ref struct
{
public void Add(int x);
abstract static T Create();
}
";
var comp1 = CreateCompilation(text1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var text2 = @"
class Program
{
static C<T> Test<T>()
where T : I1<T>, allows ref struct
{
return new C<T>() { PT = { 100, 20, 3 } };
}
}
";
var comp2 = CreateCompilation(text2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (4,17): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static C<T> Test<T>()
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "Test").WithArguments("allows ref struct constraint", "13.0").WithLocation(4, 17),
// (4,22): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// static C<T> Test<T>()
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "T").WithArguments("allows ref struct constraint", "13.0").WithLocation(4, 22),
// (5,33): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where T : I1<T>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(5, 33),
// (7,22): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// return new C<T>() { PT = { 100, 20, 3 } };
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "T").WithArguments("allows ref struct constraint", "13.0").WithLocation(7, 22)
);
comp2 = CreateCompilation(text2 + text1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12);
comp2.VerifyEmitDiagnostics(
// (5,33): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where T : I1<T>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(5, 33),
// (12,29): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where T : I1<T>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(12, 29),
// (18,29): error CS9202: Feature 'allows ref struct constraint' is not available in C# 12.0. Please use language version 13.0 or greater.
// where T : I1<T>, allows ref struct
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "ref struct").WithArguments("allows ref struct constraint", "13.0").WithLocation(18, 29)
);
comp2 = CreateCompilation(text2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular13);
comp2.VerifyEmitDiagnostics();
comp2 = CreateCompilation(text2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
comp2.VerifyEmitDiagnostics();
}
[Theory]
[CombinatorialData]
public void CollectionExpressions_01(bool addStructConstraint)
{
var text = @"
class Program
{
static T Test<T>()
where T : " + (addStructConstraint ? "struct, I1" : "I1, new()") + @", allows ref struct
{
return [ 100, 20, 3 ];
}
static void Main()
{
System.Console.Write(Test<S>().P);
System.Console.Write("" "");
System.Console.Write(((S)([ 200, 40, 6 ])).P);
}
}
public ref struct S : I1
{
public int P {get;set;}
public void Add(int x) => P += x;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => null;
}
public interface I1 : System.Collections.IEnumerable
{
public void Add(int x);
}
namespace System
{
public class Activator
{
public static T CreateInstance<T>() where T : allows ref struct => default;
}
}
";
var comp = CreateCompilation(text, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(
comp,
expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "123 246" : null,
verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics();
}
[Fact]
public void NoPiaEmbedding()
{
string pia = @"
using System;
using System.Runtime.InteropServices;
[assembly: ImportedFromTypeLib(""GeneralPIA.dll"")]
[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")]
[ComImport()]
[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58278"")]
public interface ITest29
{
void M21<T1>() where T1 : allows ref struct;
}
";
var piaCompilation = CreateCompilation(pia, options: TestOptions.ReleaseDll, assemblyName: "Pia", targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
string consumer = @"
class UsePia
{
public static void Main()
{
}
}
interface UsePia5 : ITest29
{
}
";
var compilation1 = CreateCompilation(consumer, options: TestOptions.ReleaseExe,
references: [piaCompilation.ToMetadataReference(embedInteropTypes: true)],
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
var compilation2 = CreateCompilation(consumer, options: TestOptions.ReleaseExe,
references: [piaCompilation.EmitToImageReference(embedInteropTypes: true)],
targetFramework: s_targetFrameworkSupportingByRefLikeGenerics);
System.Action<ModuleSymbol> metadataValidator =
delegate (ModuleSymbol module)
{
((PEModuleSymbol)module).Module.PretendThereArentNoPiaLocalTypes();
var itest29 = (PENamedTypeSymbol)module.GlobalNamespace.GetTypeMembers("ITest29").Single();
Assert.Equal(TypeKind.Interface, itest29.TypeKind);
var m21 = (PEMethodSymbol)itest29.GetMembers("M21").Single();
var t1 = m21.TypeParameters[0];
Assert.Equal("T1", t1.Name);
Assert.True(t1.AllowsRefLikeType);
AssertEx.Equal("void ITest29.M21<T1>() where T1 : allows ref struct", m21.ToDisplayString(SymbolDisplayFormat.TestFormatWithConstraints));
};
CompileAndVerify(compilation1, symbolValidator: metadataValidator, verify: Verification.Skipped).VerifyDiagnostics();
CompileAndVerify(compilation2, symbolValidator: metadataValidator, verify: Verification.Skipped).VerifyDiagnostics();
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/74785")]
public void Issue74785()
{
var src = @"
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
#pragma warning disable CS0649
[InlineArray(256)]
public struct BufferStruct : IBufferInterface
{
private byte _data;
[UnscopedRef]
public ReadOnlySpan<byte> Data => this;
}
interface IBufferInterface
{
[UnscopedRef]
public ReadOnlySpan<byte> Data { get; }
}
struct TestStruct<T>
where T : struct, IBufferInterface
{
T genericBuffer;
BufferStruct directBuffer;
IBufferInterface interfaceBuffer;
[UnscopedRef]
public ReadOnlySpan<byte> GetGenericBuffer1()
{
return genericBuffer.Data;
}
[UnscopedRef]
public ReadOnlySpan<byte> GetDirectBuffer1()
{
return directBuffer.Data;
}
public ReadOnlySpan<byte> GetInterfaceBuffer()
{
return interfaceBuffer.Data;
}
public ReadOnlySpan<byte> GetGenericBuffer2()
{
#line 1000
return genericBuffer.Data;
}
public ReadOnlySpan<byte> GetDirectBuffer2()
{
#line 2000
return directBuffer.Data;
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90);
comp.VerifyEmitDiagnostics(
// (1000,16): error CS8170: Struct members cannot return 'this' or other instance members by reference
// return genericBuffer.Data;
Diagnostic(ErrorCode.ERR_RefReturnStructThis, "genericBuffer").WithLocation(1000, 16),
// (2000,16): error CS8170: Struct members cannot return 'this' or other instance members by reference
// return directBuffer.Data;
Diagnostic(ErrorCode.ERR_RefReturnStructThis, "directBuffer").WithLocation(2000, 16)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/75569")]
[WorkItem("https://github.com/dotnet/roslyn/issues/75577")]
public void Iterator_01()
{
var source =
@"
using System;
using System.Collections.Generic;
static class CSharpCompilerCrash
{
public static IEnumerable<ReadOnlySpan<char>> Lines(string data)
{
yield break;
}
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90);
comp.VerifyEmitDiagnostics(
// (7,51): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs
// public static IEnumerable<ReadOnlySpan<char>> Lines(string data)
Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "Lines").WithLocation(7, 51)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/75569")]
[WorkItem("https://github.com/dotnet/roslyn/issues/75577")]
public void Iterator_02()
{
var source =
@"
using System.Collections.Generic;
static class CSharpCompilerCrash
{
static IEnumerable<A> B()
{
yield break;
}
ref struct A;
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90);
comp.VerifyEmitDiagnostics(
// (6,27): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs
// static IEnumerable<A> B()
Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(6, 27)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/75569")]
[WorkItem("https://github.com/dotnet/roslyn/issues/75577")]
public void Iterator_03()
{
var source =
@"
using System.Collections.Generic;
static class CSharpCompilerCrash
{
static IEnumerator<A> B
{
get
{
yield break;
}
}
ref struct A;
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90);
comp.VerifyEmitDiagnostics(
// (8,9): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs
// get
Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "get").WithLocation(8, 9)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/75569")]
[WorkItem("https://github.com/dotnet/roslyn/issues/75577")]
public void Iterator_04()
{
var source =
@"
#pragma warning disable CS1998 // This async method lacks 'await' operators
using System.Collections.Generic;
static class CSharpCompilerCrash
{
static async IAsyncEnumerable<RefStructA> B()
{
yield return default;
yield break;
}
ref struct RefStructA;
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (8,47): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs
// static async IAsyncEnumerable<RefStructA> B()
Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(8, 47)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/75569")]
[WorkItem("https://github.com/dotnet/roslyn/issues/75577")]
public void Iterator_05()
{
var source =
@"
#pragma warning disable CS1998 // This async method lacks 'await' operators
using System.Collections.Generic;
static class CSharpCompilerCrash
{
static async IAsyncEnumerator<RefStructA> B()
{
yield return default;
yield break;
}
ref struct RefStructA;
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (8,47): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs
// static async IAsyncEnumerator<RefStructA> B()
Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(8, 47)
);
}
[Fact]
[WorkItem("https://github.com/dotnet/roslyn/issues/75569")]
[WorkItem("https://github.com/dotnet/roslyn/issues/75577")]
public void Iterator_06()
{
var source =
@"
#pragma warning disable CS1998 // This async method lacks 'await' operators
using System.Collections.Generic;
static class CSharpCompilerCrash
{
static async IAsyncEnumerator<T> B<T>() where T : allows ref struct
{
yield return default;
yield break;
}
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (8,38): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs
// static async IAsyncEnumerator<T> B<T>() where T : allows ref struct
Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(8, 38)
);
}
}
}
|