Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check language version for more ref-struct-interfaces scenarios #73586

Merged
merged 1 commit into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1754,7 +1754,7 @@ private bool AllInterfacesContainsIEnumerable(
out bool foundMultiple)
{
CompoundUseSiteInfo<AssemblySymbol> 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))
Expand All @@ -1764,44 +1764,43 @@ 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<AssemblySymbol> useSiteInfo, out bool foundMultiple)
internal static NamedTypeSymbol GetIEnumerableOfT(
TypeSymbol type, bool isAsync, CSharpCompilation compilation, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo,
out bool foundMultiple, out bool needSupportForRefStructInterfaces)
{
NamedTypeSymbol implementedIEnumerable = null;
foundMultiple = false;

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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10722,7 +10722,7 @@ protected override void VisitForEachExpression(BoundForEachStatement node)
// This is case 4. We need to look for the IEnumerable<T> that this reinferred expression implements,
// so that we pick up any nested type substitutions that could have occurred.
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.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);
}
Expand Down
38 changes: 32 additions & 6 deletions src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>
Diagnostic(ErrorCode.ERR_FeatureInPreview, "IEnumerable<int>").WithArguments("ref struct interfaces").WithLocation(4, 23)
);

comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.RegularNext);
comp2.VerifyEmitDiagnostics();
Expand Down Expand Up @@ -15770,18 +15784,16 @@ public void AwaitForeach_IAsyncEnumerableT_LanguageVersion_03()

public ref struct S : IAsyncEnumerable<int>
{
IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(CancellationToken token = default) => throw null;
IAsyncEnumerator<int> IAsyncEnumerable<int>.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())
{
Expand All @@ -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<int>
Diagnostic(ErrorCode.ERR_FeatureInPreview, "IAsyncEnumerable<int>").WithArguments("ref struct interfaces").WithLocation(6, 23)
);

comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, parseOptions: TestOptions.RegularNext);
comp2.VerifyEmitDiagnostics();
Expand Down