From ef0bb754f4f11cf9a64ebe26e5e822c3f82da409 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Mon, 20 May 2024 08:25:04 -0700 Subject: [PATCH] Check language version for more ref-struct-interfaces scenarios --- .../Portable/Binder/ForEachLoopBinder.cs | 29 +++++++------- .../Portable/FlowAnalysis/NullableWalker.cs | 2 +- .../Test/Emit3/RefStructInterfacesTests.cs | 38 ++++++++++++++++--- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index e37917d6a345c..888094f169b44 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -1754,7 +1754,7 @@ private bool AllInterfacesContainsIEnumerable( out bool foundMultiple) { CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - NamedTypeSymbol implementedIEnumerable = GetIEnumerableOfT(type, isAsync, Compilation, ref useSiteInfo, out foundMultiple); + NamedTypeSymbol implementedIEnumerable = GetIEnumerableOfT(type, isAsync, Compilation, ref useSiteInfo, out foundMultiple, out bool needSupportForRefStructInterfaces); // Prefer generic to non-generic, unless it is inaccessible. if (((object)implementedIEnumerable == null) || !this.IsAccessible(implementedIEnumerable, ref useSiteInfo)) @@ -1764,31 +1764,28 @@ private bool AllInterfacesContainsIEnumerable( if (!isAsync) { var implementedNonGeneric = this.Compilation.GetSpecialType(SpecialType.System_Collections_IEnumerable); - if ((object)implementedNonGeneric != null) + if ((object)implementedNonGeneric != null && + this.Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(type, implementedNonGeneric, ref useSiteInfo, out needSupportForRefStructInterfaces)) { - var implements = this.Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(type, implementedNonGeneric, ref useSiteInfo, out bool needSupportForRefStructInterfaces); - - if (implements) - { - implementedIEnumerable = implementedNonGeneric; - - if (needSupportForRefStructInterfaces && - type.ContainingModule != Compilation.SourceModule) - { - CheckFeatureAvailability(collectionSyntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics); - } - } + implementedIEnumerable = implementedNonGeneric; } } } + if (implementedIEnumerable is not null && needSupportForRefStructInterfaces && type.ContainingModule != Compilation.SourceModule) + { + CheckFeatureAvailability(collectionSyntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics); + } + diagnostics.Add(collectionSyntax, useSiteInfo); builder.CollectionType = implementedIEnumerable; return (object)implementedIEnumerable != null; } - internal static NamedTypeSymbol GetIEnumerableOfT(TypeSymbol type, bool isAsync, CSharpCompilation compilation, ref CompoundUseSiteInfo useSiteInfo, out bool foundMultiple) + internal static NamedTypeSymbol GetIEnumerableOfT( + TypeSymbol type, bool isAsync, CSharpCompilation compilation, ref CompoundUseSiteInfo useSiteInfo, + out bool foundMultiple, out bool needSupportForRefStructInterfaces) { NamedTypeSymbol implementedIEnumerable = null; foundMultiple = false; @@ -1796,12 +1793,14 @@ internal static NamedTypeSymbol GetIEnumerableOfT(TypeSymbol type, bool isAsync, if (type.TypeKind == TypeKind.TypeParameter) { var typeParameter = (TypeParameterSymbol)type; + needSupportForRefStructInterfaces = typeParameter.AllowsRefLikeType; var allInterfaces = typeParameter.EffectiveBaseClass(ref useSiteInfo).AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo) .Concat(typeParameter.AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo)); GetIEnumerableOfT(allInterfaces, isAsync, compilation, ref @implementedIEnumerable, ref foundMultiple); } else { + needSupportForRefStructInterfaces = type.IsRefLikeType; GetIEnumerableOfT(type.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo), isAsync, compilation, ref @implementedIEnumerable, ref foundMultiple); } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index c139e3daf28c4..81109ab290733 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -10722,7 +10722,7 @@ protected override void VisitForEachExpression(BoundForEachStatement node) // This is case 4. We need to look for the IEnumerable that this reinferred expression implements, // so that we pick up any nested type substitutions that could have occurred. var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; - targetTypeWithAnnotations = TypeWithAnnotations.Create(ForEachLoopBinder.GetIEnumerableOfT(resultType, isAsync, compilation, ref discardedUseSiteInfo, out bool foundMultiple)); + targetTypeWithAnnotations = TypeWithAnnotations.Create(ForEachLoopBinder.GetIEnumerableOfT(resultType, isAsync, compilation, ref discardedUseSiteInfo, out bool foundMultiple, needSupportForRefStructInterfaces: out _)); Debug.Assert(!foundMultiple); Debug.Assert(targetTypeWithAnnotations.HasType); } diff --git a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs index d9a9090539145..7cc8bda303841 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs @@ -8536,7 +8536,21 @@ static void Main() } "; var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12); - comp2.VerifyEmitDiagnostics(); + + // 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 CS8652: The feature 'ref struct interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (var i in new S()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "new S()").WithArguments("ref struct interfaces").WithLocation(6, 27) + ); + + comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12); + comp2.VerifyEmitDiagnostics( + // (4,23): error CS8652: The feature 'ref struct interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public ref struct S : IEnumerable + Diagnostic(ErrorCode.ERR_FeatureInPreview, "IEnumerable").WithArguments("ref struct interfaces").WithLocation(4, 23) + ); comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.RegularNext); comp2.VerifyEmitDiagnostics(); @@ -15770,18 +15784,16 @@ public void AwaitForeach_IAsyncEnumerableT_LanguageVersion_03() public ref struct S : IAsyncEnumerable { - IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token = default) => throw null; + IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) => throw null; } "; var comp1 = CreateCompilation(src1, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics); var src2 = @" -using System.Threading.Tasks; - class C { - static async Task Main() + static async System.Threading.Tasks.Task Main() { await foreach (var i in new S()) { @@ -15790,7 +15802,21 @@ static async Task Main() } "; var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12); - comp2.VerifyEmitDiagnostics(); + + // 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 CS8652: The feature 'ref struct interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var i in new S()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "new S()").WithArguments("ref struct interfaces").WithLocation(6, 33) + ); + + comp2 = CreateCompilation(src1 + src2, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.Regular12); + comp2.VerifyEmitDiagnostics( + // (6,23): error CS8652: The feature 'ref struct interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public ref struct S : IAsyncEnumerable + Diagnostic(ErrorCode.ERR_FeatureInPreview, "IAsyncEnumerable").WithArguments("ref struct interfaces").WithLocation(6, 23) + ); comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.RegularNext); comp2.VerifyEmitDiagnostics();