Skip to content

Commit

Permalink
Check language version for more scenarios (#73546)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlekseyTs authored May 17, 2024
1 parent fb3be63 commit 71887d8
Show file tree
Hide file tree
Showing 8 changed files with 1,518 additions and 191 deletions.
9 changes: 8 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
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);
}

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

0 comments on commit 71887d8

Please sign in to comment.