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 scenarios #73546

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -6158,8 +6158,15 @@ private bool CollectionInitializerTypeImplementsIEnumerable(TypeSymbol initializ
// NOTE: type to the predefined System.Collections.IEnumerable type, so we do the same.

CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var result = Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(initializerType, collectionsIEnumerableType, ref useSiteInfo);
var result = Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(initializerType, collectionsIEnumerableType, ref useSiteInfo, out bool needSupportForRefStructInterfaces);
diagnostics.Add(node, useSiteInfo);

if (needSupportForRefStructInterfaces &&
initializerType.ContainingModule != Compilation.SourceModule)
{
CheckFeatureAvailability(node, MessageID.IDS_FeatureRefStructInterfaces, diagnostics);
}

Comment on lines +6164 to +6169
Copy link
Member

@333fred 333fred May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this check is used in several places. Could we consider extracting a helper CheckFeatureAvailabilityForUsage or soemthing similar? #WontFix

return result;
}
else
Expand Down
17 changes: 15 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1259,10 +1259,17 @@ bool implementsInterface(TypeSymbol enumeratorType, bool isAsync, BindingDiagnos

bool result = this.Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(enumeratorType,
targetInterface,
ref useSiteInfo);
ref useSiteInfo,
out bool needSupportForRefStructInterfaces);

diagnostics.Add(syntax, useSiteInfo);

if (needSupportForRefStructInterfaces &&
enumeratorType.ContainingModule != Compilation.SourceModule)
{
CheckFeatureAvailability(syntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics);
}

return result;
}
}
Expand Down Expand Up @@ -1759,11 +1766,17 @@ private bool AllInterfacesContainsIEnumerable(
var implementedNonGeneric = this.Compilation.GetSpecialType(SpecialType.System_Collections_IEnumerable);
if ((object)implementedNonGeneric != null)
{
var implements = this.Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(type, implementedNonGeneric, ref useSiteInfo);
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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2967,12 +2967,24 @@ internal bool ImplementsVarianceCompatibleInterface(NamedTypeSymbol derivedType,
return ImplementsVarianceCompatibleInterface((TypeSymbol)derivedType, baseType, ref useSiteInfo);
}

internal bool HasImplicitConversionToOrImplementsVarianceCompatibleInterface(TypeSymbol typeToCheck, NamedTypeSymbol targetInterfaceType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
internal bool HasImplicitConversionToOrImplementsVarianceCompatibleInterface(TypeSymbol typeToCheck, NamedTypeSymbol targetInterfaceType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, out bool needSupportForRefStructInterfaces)
{
Debug.Assert(targetInterfaceType.IsErrorType() || targetInterfaceType.IsInterface);

return ClassifyImplicitConversionFromType(typeToCheck, targetInterfaceType, ref useSiteInfo).IsImplicit ||
IsRefLikeOrAllowsRefLikeTypeImplementingVarianceCompatibleInterface(typeToCheck, targetInterfaceType, ref useSiteInfo);
if (ClassifyImplicitConversionFromType(typeToCheck, targetInterfaceType, ref useSiteInfo).IsImplicit)
{
needSupportForRefStructInterfaces = false;
return true;
}

if (IsRefLikeOrAllowsRefLikeTypeImplementingVarianceCompatibleInterface(typeToCheck, targetInterfaceType, ref useSiteInfo))
{
needSupportForRefStructInterfaces = true;
return true;
}

needSupportForRefStructInterfaces = false;
return false;
}

private bool IsRefLikeOrAllowsRefLikeTypeImplementingVarianceCompatibleInterface(TypeSymbol typeToCheck, NamedTypeSymbol targetInterfaceType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
Expand All @@ -2989,12 +3001,24 @@ private bool IsRefLikeOrAllowsRefLikeTypeImplementingVarianceCompatibleInterface
return false;
}

internal bool HasImplicitConversionToOrImplementsVarianceCompatibleInterface(BoundExpression expressionToCheck, NamedTypeSymbol targetInterfaceType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
internal bool HasImplicitConversionToOrImplementsVarianceCompatibleInterface(BoundExpression expressionToCheck, NamedTypeSymbol targetInterfaceType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, out bool needSupportForRefStructInterfaces)
{
Debug.Assert(targetInterfaceType.IsErrorType() || targetInterfaceType.IsInterface);

return ClassifyImplicitConversionFromExpression(expressionToCheck, targetInterfaceType, ref useSiteInfo).IsImplicit ||
(expressionToCheck.Type is TypeSymbol typeToCheck && IsRefLikeOrAllowsRefLikeTypeImplementingVarianceCompatibleInterface(typeToCheck, targetInterfaceType, ref useSiteInfo));
if (ClassifyImplicitConversionFromExpression(expressionToCheck, targetInterfaceType, ref useSiteInfo).IsImplicit)
{
needSupportForRefStructInterfaces = false;
return true;
}

if (expressionToCheck.Type is TypeSymbol typeToCheck && IsRefLikeOrAllowsRefLikeTypeImplementingVarianceCompatibleInterface(typeToCheck, targetInterfaceType, ref useSiteInfo))
{
needSupportForRefStructInterfaces = true;
return true;
}

needSupportForRefStructInterfaces = false;
return false;
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
28 changes: 19 additions & 9 deletions src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,7 @@ bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeI
NamedTypeSymbol disposableInterface = getDisposableInterface(hasAwait);
Debug.Assert((object)disposableInterface != null);

CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = originalBinder.GetNewCompoundUseSiteInfo(diagnostics);
bool implementsIDisposable = implementsInterface(fromExpression, disposableInterface, ref useSiteInfo);

diagnostics.Add(syntax, useSiteInfo);
bool implementsIDisposable = implementsInterface(fromExpression, disposableInterface, diagnostics);

if (implementsIDisposable)
{
Expand All @@ -255,8 +252,7 @@ bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeI
{
// Retry with a different assumption about whether the `using` is async
NamedTypeSymbol alternateInterface = getDisposableInterface(!hasAwait);
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
bool implementsAlternateIDisposable = implementsInterface(fromExpression, alternateInterface, ref discardedUseSiteInfo);
bool implementsAlternateIDisposable = implementsInterface(fromExpression, alternateInterface, BindingDiagnosticBag.Discarded);

ErrorCode errorCode = implementsAlternateIDisposable
? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync)
Expand All @@ -268,19 +264,33 @@ bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeI
return false;
}

bool implementsInterface(bool fromExpression, NamedTypeSymbol targetInterface, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
bool implementsInterface(bool fromExpression, NamedTypeSymbol targetInterface, BindingDiagnosticBag diagnostics)
{
var conversions = originalBinder.Conversions;
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = originalBinder.GetNewCompoundUseSiteInfo(diagnostics);
bool result;
bool needSupportForRefStructInterfaces;

if (fromExpression)
{
Debug.Assert(expressionOpt is { });
return conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(expressionOpt, targetInterface, ref useSiteInfo);
result = conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(expressionOpt, targetInterface, ref useSiteInfo, out needSupportForRefStructInterfaces);
}
else
{
Debug.Assert(declarationTypeOpt is { });
return conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(declarationTypeOpt, targetInterface, ref useSiteInfo);
result = conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(declarationTypeOpt, targetInterface, ref useSiteInfo, out needSupportForRefStructInterfaces);
}

diagnostics.Add(syntax, useSiteInfo);

if (needSupportForRefStructInterfaces &&
(fromExpression ? expressionOpt!.Type : declarationTypeOpt)!.ContainingModule != originalBinder.Compilation.SourceModule)
{
CheckFeatureAvailability(syntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics);
}

return result;
}

NamedTypeSymbol getDisposableInterface(bool isAsync)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ private bool TryGetDisposeMethod(SyntaxNode forEachSyntax, ForEachEnumeratorInfo
var conversions = _factory.CurrentFunction.ContainingAssembly.CorLibrary.TypeConversions;

CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo();
implementsInterface = conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(enumeratorType, idisposableTypeSymbol, ref useSiteInfo);
implementsInterface = conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(enumeratorType, idisposableTypeSymbol, ref useSiteInfo, out _);
_diagnostics.Add(forEachSyntax, useSiteInfo);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ private BoundExpression MakeObjectInitializerMemberAccess(
#if DEBUG
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
Debug.Assert(_compilation.Conversions.ClassifyConversionFromType(rewrittenReceiver.Type, memberSymbol.ContainingType, isChecked: false, ref discardedUseSiteInfo).IsImplicit ||
_compilation.Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(rewrittenReceiver.Type, memberSymbol.ContainingType, ref discardedUseSiteInfo));
_compilation.Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(rewrittenReceiver.Type, memberSymbol.ContainingType, ref discardedUseSiteInfo, out _));
// It is possible there are use site diagnostics from the above, but none that we need report as we aren't generating code for the conversion
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1881,7 +1881,8 @@ private IForLoopOperation CreateBoundForStatementOperation(BoundForStatement bou
compilation.Conversions.
HasImplicitConversionToOrImplementsVarianceCompatibleInterface(enumeratorInfoOpt.GetEnumeratorInfo.Method.ReturnType,
iDisposable,
ref discardedUseSiteInfo) :
ref discardedUseSiteInfo,
needSupportForRefStructInterfaces: out _) :
false,
enumeratorInfoOpt.PatternDisposeInfo?.Method.GetPublicSymbol(),
BoundNode.GetConversion(enumeratorInfoOpt.CurrentConversion, enumeratorInfoOpt.CurrentPlaceholder),
Expand Down
Loading